Hallo,
weshalb ändert sich im Memory Dump nichts wenn ich ein Array fülle(und
während ich fülle debugge)?
Ich hätte eine Änderung an der Adresse 0x22ff38 bzw. den darauf
folgenden Adressen erwartet!
Mein Quelltext:
Hättest du mal ein vernünftiges C-Buch in der Hand gehabt, wüsstest du,
dass &zahlen nicht auf das Array im Speicher zeigt, sondern auf den
Zeiger, der auf das Array zeigt. Du willst dir aber den Speicher an der
Stelle ansehen, die in "zahlen" steht.
Rolf Magnus schrieb:> Ist zwar nicht die Antwort, aber du solltest vermeiden, in ein 5> Elemente großes Array 6 Werte zu schreiben.
Richtig, das mit dem Buch gilt auch hier.
vn nn schrieb:> ... sondern auf den> Zeiger, der auf das Array zeigt.
Diesen Zeiger gibt es bei Arrays gar nicht.
Das ist ja einer der Unterschiede zwischen Arrays und Zeigern.
Im Watches-Fenster kann man auch die Werte über dem i = 1 sehen.
Die passen zu dem Hexdump im Memory-Fenster.
vn nn schrieb:> Hättest du mal ein vernünftiges C-Buch in der Hand gehabt, wüsstest du,> dass &zahlen nicht auf das Array im Speicher zeigt, sondern auf den> Zeiger, der auf das Array zeigt.
Den aber gibt es hier nicht. In was für einem vernünftigen C-Buch meinst
Du diese Information gefunden zu haben?
Was den nun?
zeigt &zeiger nun auf einen Zeiger, der auf das Array zeigt?
Die Werte im HEX DUMP stimmen mit denen im Watches-Fenster überein wenn
ich noch nichts in das Array geschrieben hab. Wenn ich jedoch jetzt
zahlen in das Array schreibe Z.B
1 2 3 4 5
index: 0 1 2 3 4
ämdert sich im HEX DUMP nichts! weshalb?
Joe M. schrieb:> zeigt &zeiger nun auf einen Zeiger, der auf das Array zeigt?
Nein! Nein! Nein!
Ein Array ist kein Zeiger.
Hast du denn auch mal wieder auf Go geklickt?
DirkB schrieb:> Also gibt es auch keinen Zeiger.
Sieh dir die Array-Syntax genau an, dann wirst du sehen, daß man mit dem
Namen eines Arrays dasselbe machen kann, wie mit einem Zeiger auf ein
Array.
Der Grund ist, daß ein Array-Name eine Zeigerkonstante ist, wie 5 eine
Integer-Konstante ist.
Diese Array-Pointer-Konstanten werden - wie andere Konstanten auch -
üblicherweise als Immediate-Operanden direkt im Maschinencode
gespeichert.
Das ist ein Stück der Eleganz von C.
Uhu Uhuhu schrieb:> Der Grund ist, daß ein Array-Name eine Zeigerkonstante ist, wie 5 eine> Integer-Konstante ist.
Nein. Dann würde sizeof(Array) nämlich die Größe eines Zeigers
zurückgeben, was es aber nicht tut. Außerdem ist &Array eben wie schon
gesagt wurde nicht die Adresse eines Zeigers und auch nicht vom Typ
"Zeiger auf Zeiger".
Es ist eher so, daß der Array-Name bei den meisten (aber eben nicht
allen) Operationen, die man damit machen kann, implizit in einen Zeiger
konvertiert wird, also eher so, wie man 5 auch einer double-Variable
zuweisen kann und der Compiler dann automatisch eine 5.0 draus macht.
Deshalb ist in der 5 aber nicht automatisch eine 5.0 enthalten, und
genausowenig gibt's beim Array einen Zeiger.
> Diese Array-Pointer-Konstanten werden - wie andere Konstanten auch -> üblicherweise als Immediate-Operanden direkt im Maschinencode> gespeichert.
Das stelle ich mir bei einem lokalen Array etwas schwierig vor. Da ist
die Adresse nämlich erst zur Laufzeit bekannt.
> Das ist ein Stück der Eleganz von C.
Auch wenn es wohl die Intention dahinter war, ist es meiner Meinung nach
nicht besonders elegant. Das erkennt man auch daran, daß Arrays und
Zeiger für Anfänger eines der am schwierigsten zu verstehenden Themen
beim Erlernen der Sprache sind.
Rolf Magnus schrieb:> Dann würde sizeof(Array) nämlich die Größe eines Zeigers> zurückgeben, was es aber nicht tut.
Das ist eine der wenigen Stellen, an denen die Vereinheitlichung nicht
ganz klappt.
> Außerdem ist &Array eben wie schon gesagt wurde nicht die Adresse eines> Zeigers
&konstante ist nicht definiert; es gibt sie nicht, weil Konstanten
sozusagen allgegenwärtig sind.
> Das stelle ich mir bei einem lokalen Array etwas schwierig vor. Da ist> die Adresse nämlich erst zur Laufzeit bekannt.
In dem Fall ist es ein konstanter Offset auf ein Indexregister, häufig
dem Stackpointer - also kein wesentlicher Unterschied.
>> Das ist ein Stück der Eleganz von C.>> Auch wenn es wohl die Intention dahinter war, ist es meiner Meinung nach> nicht besonders elegant. Das erkennt man auch daran, daß Arrays und> Zeiger für Anfänger eines der am schwierigsten zu verstehenden Themen> beim Erlernen der Sprache sind.
Andersrum wird ein Schuh draus: Das Konzept Variablenname ist relativ
schwer verständlich und weil den Anfängern die Gemeinsamkeiten von
Pointern und Array-Namen nicht frühzeitig durchsichtig gemacht werden,
bleibt ein Array-Name undurchsichtig.
Zudem sollte man Fragen der Didaktik nicht mit Fragen Semantik
vermengen.
Aus eigener Erfahrung kann ich nur sagen, daß mir das
Array-/Pointer-Konzept auf Anhieb klar war, als ich die Beschreibung vor
Urzeiten bei Kerninghan/Ritchie gelesen hatte. Reichlich ASM-Hintergrund
war dazu natürlich überaus hilfreich.
Im Quelltext des Threadstarters wird
&array
nicht verwendet. Sondern --und das ist ein essentieller Unterschied--
&array[index]
Und damit ist die Diskussion ziemlich am Thema vorbei.
Rufus Τ. Firefly schrieb:> Im Quelltext des Threadstarters wird>> &array>> nicht verwendet. Sondern --und das ist ein essentieller Unterschied-->> &array[index]>> Und damit ist die Diskussion ziemlich am Thema vorbei.
Das Unverständnis für Arrays und Pointer ist das Kernthema und das zu
beseitigen, kann kaum am Thema vorbei sein...
Uhu Uhuhu schrieb:>> Außerdem ist &Array eben wie schon gesagt wurde nicht die Adresse eines>> Zeigers>> &konstante ist nicht definiert; es gibt sie nicht, weil Konstanten> sozusagen allgegenwärtig sind.
&Array gibt es aber. Und es ergibt wie bei jeder anderen Variable auch
deren Adresse.
>> Das stelle ich mir bei einem lokalen Array etwas schwierig vor. Da ist>> die Adresse nämlich erst zur Laufzeit bekannt.>> In dem Fall ist es ein konstanter Offset auf ein Indexregister, häufig> dem Stackpointer - also kein wesentlicher Unterschied.
Wo unterscheiden sich denn hier Arrays von irgendwelchen anderen
Variablen? Bei denen spreche ich doch auch nicht davon, daß es da immer
eine dazugehörige Pointerkonstante oder sowas gibt.
Mindestens seit C99 gibt's aber auch Beispiele, wo auch das mit dem
konstanten Offset nicht klappt.
1
voidfunc(inti,intj)
2
{
3
inta[i];
4
intb[j];
5
}
Hier ist auch der Offset zum Stackpointer erst zur Laufzeit bekannt.
>>> Das ist ein Stück der Eleganz von C.>>>> Auch wenn es wohl die Intention dahinter war, ist es meiner Meinung nach>> nicht besonders elegant. Das erkennt man auch daran, daß Arrays und>> Zeiger für Anfänger eines der am schwierigsten zu verstehenden Themen>> beim Erlernen der Sprache sind.>> Andersrum wird ein Schuh draus: Das Konzept Variablenname ist relativ> schwer verständlich und weil den Anfängern die Gemeinsamkeiten von> Pointern und Array-Namen nicht frühzeitig durchsichtig gemacht werden,> bleibt ein Array-Name undurchsichtig.
Was soll an Variablennamen denn schwer verständlich sein?
Was ich dagegen z.B. als für einen Anfänger schwer zu durchschauen
empfinde, ist, warum bei:
1
#include<stdio.h>
2
voidfunc(intarray[10])
3
{
4
printf("%d\n",sizeofarray);
5
}
6
7
intmain()
8
{
9
intarray[10];
10
func(array);
11
}
nicht 10 ausgegeben wird. Und das liegt eben daran, daß heimlich aus dem
Array ein Zeiger gemacht wird. Dazu kommt hier noch der "geniale"
Schachzug bei, daß bei Funktionsparametern (aber nur dort) auf einmal
die Array-Syntax verwendet wird, um einen Zeiger zu definieren und nicht
etwa ein Array und daß die 10 in den eckigen Klammern einfach komplett
ignoriert wird.
> Zudem sollte man Fragen der Didaktik nicht mit Fragen Semantik> vermengen.
Genauso wie die Definition der Sprache und die darunterliegende
Impelementation. In C ist definiert, daß das Array in einen Zeiger auf
das erste Element konvertiert wird. Die Konstante mit der Adresse drin
ist die Art, wie es meist implementiert wird. Solche
Implementationsdetails sind aber für jemanden, der die Sprache lernt,
erstmal irelevant.
> Aus eigener Erfahrung kann ich nur sagen, daß mir das Array-/Pointer-> Konzept auf Anhieb klar war, als ich die Beschreibung vor Urzeiten bei> Kerninghan/Ritchie gelesen hatte. Reichlich ASM-Hintergrund war dazu> natürlich überaus hilfreich.
Ich weiß ehrlich gesagt gar nicht mehr, wie das bei mir war. Aber ich
sehe in Foren und Newsgroups bei C-Einsteigern sehr viele Fragen, die
erkennen lassen, daß Zeiger und Arrays nicht richtig verstanden wurden.
Ich würde schätzen, daß es mit Abstand der Teil ist, bei dem am
häufigsten Verständisprobleme auftreten.
C hat angefangen als eine Art Super-Assembler und für
Assemblerprogrammierer ist ein Pointer wirklich nichts aufregendes.
Was K&R mit dieser Vereinheitlichung von Arrays und Pointern erreicht
haben, ist uniforme Behandlung von Arrays, gleichgültig, ob sie über
Pointer adressiert werden, oder nicht.
1
inta[5];
2
int*pa=a;
Mit der Zuweisung an pa geht die Längeniformation, die der Compiler zu a
mitführt verloren, das paßte aber durchaus zur alten C-Philosophie, daß
sich der Compiler um Feldgrenzen sowieso nicht kümmert.
Gewonnen wird mit dem Kniff, daß Arrays und Pointer (void * gab es in
den frühen C-Versionen sowieso noch nicht) mit derselben Syntax
bearbeitet werden können:
1
pa[5]==a[5]
2
*(pa+5)==a[5]
3
*(pa+5)==*(a+5)
Die Array-Schreibweise wird damit auf die Pointer-Arithmetik
zurückgeführt und für den Anfänger ist das m.A. eine unverzichtbare
Einsicht, die den Umgang mit Pointern sehr erleichtert.
> Was ich dagegen z.B. als für einen Anfänger schwer zu durchschauen> empfinde, ist, warum bei:> ...> nicht 10 ausgegeben wird.
Der Compiler speichert zu Arrays als Zusatzinformation die Länge in
Elementen des Grundtyps - das muß er auf jeden Fall tun, denn anders
kann er das Speicherlayout nicht brechnen.
Wenn die Array-Adresse einem Pointer zugewiesen wird, dann geht diese
Zusatzinformation verloren, weil über Pointer eben nur der Grundtyp und
irgendwelche Modifier gespeichert werden. Werte-Tracking ist zumindest
sehr aufwendig, wenn nicht unmöglich und der C-Compiler sollte auf einer
PDP-8 laufen mit 4096 Worten zu je 12 Bit, nicht auf einem heutigen
Rechner mit Gigabytes RAM und Taktraten, von denen damals keiner zu
träumen wagte.
> Solche Implementationsdetails sind aber für jemanden, der die Sprache> lernt, erstmal irelevant.
Es macht zwar Mathematikern und Informatikern einen Höllenspaß,
irgendwelche verzwickten Konstrukte völlig unvermittelt auf Anfänger
herunterregnen zu lassen, aber didaktisch ist das - zumindest für meine
Begriffe - grober Unfug.
Wie eigentlich immer, schadet ein Blick in die Geschichte auch hier
nicht und liefert aufeinander aufbauende und leicht nachzuvollziehende
Einsichten und wer so tut, als seien die Axiome schon immer da gewesen,
der zieht tendenziell nur geschichtslose Trottel heran.
> Ich weiß ehrlich gesagt gar nicht mehr, wie das bei mir war. Aber ich> sehe in Foren und Newsgroups bei C-Einsteigern sehr viele Fragen, die> erkennen lassen, daß Zeiger und Arrays nicht richtig verstanden wurden.> Ich würde schätzen, daß es mit Abstand der Teil ist, bei dem am> häufigsten Verständisprobleme auftreten.
Dem kann m.A. leicht abgeholfen werden, wenn man etwas Zeit in die
Vermittlung der Hintergründe investiert.
Trotzdem ergibt bei deinem Beispiel
&a etwas anderes als &pa
a = pa ist nicht erlaubt,
Und daraus folgt auch:
pa++ ist möglich, im Gegensatz zu a++
Denn es gibt keine Speicherstelle für die Adresse von a, auf die du von
C aus zugreifen kannst.
DirkB schrieb:> Trotzdem ergibt bei deinem Beispiel> &a etwas anderes als &pa
Ist das jetzt etwa völlig unerwartet?
> a = pa ist nicht erlaubt,
Hab ich das behauptet?
> pa++ ist möglich, im Gegensatz zu a++
Konstanten sind nun eben mal unveränderlich...
> Denn es gibt keine Speicherstelle für die Adresse von a, auf die du von> C aus zugreifen kannst.
Habe ich das behauptet?
Uhu Uhuhu schrieb:> C hat angefangen als eine Art Super-Assembler und für> Assemblerprogrammierer ist ein Pointer wirklich nichts aufregendes.>> Was K&R mit dieser Vereinheitlichung von Arrays und Pointern erreicht> haben, ist uniforme Behandlung von Arrays, gleichgültig, ob sie über> Pointer adressiert werden, oder nicht.> int a[5];> int *pa = a;
Ja, allerdings schafft das eine Inkonsistenz zu anderen Typen, wo es
sowas nicht gibt. Der Grund dafür, das hinzunehmen, war vermutlich
folgender:
1
inta[5];
2
int(*pa)[5]=&a;
3
(*pa)[2]=10;
So ist die Behandlung des Arrays exakt gleich wie die von
nicht-Array-Tpen. Es gibt nur einen Nachteil: Im Zeigertyp steht die
Arraygröße drin, und ich kann ihn daher ausschließlich für Arrays der
Größe 5 benuten. sizeof *pa ergibt 5*sizeof(int).
Also mußte man sich was einfallen lassen, wie man einen Zeiger bekommt,
über den man auf Arrays zugreifen kann, ohne bei der Definition des
Zeigers schon die Array-Größe festlegen zu müssen. So kam die Idee mit
dem Zeiger auf das erste Element, und man hat dann in Konsequenz den
Zugriff über den Zeiger noch gleich machen müssen wie über das Array,
was aber leider auch nicht überall durchgezogen werden konnte. So
ergeben sich noch mehr Inkonsistenzen. Ich sehe das ganze daher nicht
als Genialität, sondern eher als notwendiges Übel.
Uhu Uhuhu schrieb:> Die Array-Schreibweise wird damit auf die Pointer-Arithmetik> zurückgeführt und für den Anfänger ist das m.A. eine unverzichtbare> Einsicht, die den Umgang mit Pointern sehr erleichtert.
Meiner Ansicht nach ist es genau das Gegenteil. Es ist eine Hürde für
das Verständnis von Arrays und Pointern.
Uhu Uhuhu schrieb:> Wenn die Array-Adresse einem Pointer zugewiesen wird,
Du meinst die Adresse des ersten Elements. Die Array-Adresse hat zwar
den selben Wert, aber einen anderen Typ.
> dann geht diese Zusatzinformation verloren, weil über Pointer eben nur> der Grundtyp und irgendwelche Modifier gespeichert werden. Werte-Tracking> ist zumindest sehr aufwendig, wenn nicht unmöglich
Bei Funktionsparametern ist es unmöglich, denn die Funktion kann ja
jedesmal mit einer anderen Array-Größe aufgerufen werden.
Uhu Uhuhu schrieb:>> Solche Implementationsdetails sind aber für jemanden, der die Sprache>> lernt, erstmal irelevant.>> Es macht zwar Mathematikern und Informatikern einen Höllenspaß,> irgendwelche verzwickten Konstrukte völlig unvermittelt auf Anfänger> herunterregnen zu lassen, aber didaktisch ist das - zumindest für meine> Begriffe - grober Unfug.
Ja, eben genau deshalb sollte man sich ja nicht darin verlieren, dem
Lernenden alle Details zu erklären, wie die Sprache üblicherweise von
einem Compiler implementiert wird, sondern erstmal auf Sprachebene
bleiben. Man fängt ja auch nicht damit an, Funktionen über irgendwelche
Stackframes und Registerzuordnungen zu erklären. Die sind (deutlich)
später für ein tiefergehendes Verständnis durchaus hilfreich, aber nicht
für den Anfänger. Programmiersprachen schaffen eine Abstraktionsebene,
die ja gerade deshalb da ist, damit man sich mit diesen Details nicht
beschäftigen muß.
>> Ich weiß ehrlich gesagt gar nicht mehr, wie das bei mir war. Aber ich>> sehe in Foren und Newsgroups bei C-Einsteigern sehr viele Fragen, die>> erkennen lassen, daß Zeiger und Arrays nicht richtig verstanden wurden.>> Ich würde schätzen, daß es mit Abstand der Teil ist, bei dem am>> häufigsten Verständisprobleme auftreten.>> Dem kann m.A. leicht abgeholfen werden, wenn man etwas Zeit in die> Vermittlung der Hintergründe investiert.
Wenn das für das Erlernen der Sprache notwendig ist, zeigt das aber, daß
es sich um ein schwer veständliches Thema handelt.
Rolf Magnus schrieb:> Programmiersprachen schaffen eine Abstraktionsebene,> die ja gerade deshalb da ist, damit man sich mit diesen Details nicht> beschäftigen muß.
Abstraktionen sind gut, wenn man die Hintergründe verstanden hat, nicht
umgekehrt. Die Sprachentwickler haben ja auch nicht ein paar
Definitionen ausgeklinkt und anshließend nach Anwendungsfällen gesucht.
Was ich an den µCs so reizvoll finde, ist ihre Überschaubarkeit. So
ähnlich war vor 40 Jahren die ganze Computerei.
Bei der Abstrahiererei wurde m.A. schon gerne mal übers Ziel
rausgeschossen. Die Folge ist, daß man einen ewig komplizierten Kloß,
dessen Struktur sich bestenfalls ganz langsam erschließt, oben auf einer
mehr oder weniger simpel gestrickten Maschine liegen hat, der die Sicht
auf das versperrt, was man mit seiner Anwendung eigentlich treibt.
Die Grundideen von C kann man jedenfalls sehr schön an einem ganz
simplem Maschinenmodell regelrecht erfahrbar machen.
Aber diese Rangehensweise ist inkompatibel mit der Rausprüf-Ideologie...
Rolf Magnus schrieb:> Uhu Uhuhu schrieb:>> Die Array-Schreibweise wird damit auf die Pointer-Arithmetik>> zurückgeführt und für den Anfänger ist das m.A. eine unverzichtbare>> Einsicht, die den Umgang mit Pointern sehr erleichtert.>> Meiner Ansicht nach ist es genau das Gegenteil. Es ist eine Hürde für> das Verständnis von Arrays und Pointern.
Meiner Meinung nach besteht das Hauptproblem, dass man im Umgang mit
Neulingen nicht konsequent die Begriffe "Adresse" und "Pointer-Variable"
trennt. Alleine durch diese sprachliche Unterscheidung lassen sich gut
50% der ganzen Pointer-Probleme aus der Welt schaffen.