Als ich micht jetzt nur mehr beim Try&Error erwischte musste ich
aufgeben und muss mit dem Thema 'Pointer' nochmals bei 0 beginnen.
Und wenn Pointer in ein Struct-Array zeigen soll die auch Pointer zu
anderen Struct-Array mit Funktionspointern enthalten dann bringt alles
Probieren nichts mehr...
Mein Wissen über Pointer ist noch jung und anscheinend hab ich von Grund
auf falsches gelernt.
Folgendes glaubte ich zu wissen:
1
// 'meinCharArray' ist ein Pointer und zeigt auf das erste 'char'("a") im Speicherfeld das 6+1 Bytes belegt.
2
charmeinCharArray[]="abcdef";
3
4
// 'meineFunktion' ist ein Pointer und zeigt auf die Einsprungadresse dieser Prozedur
5
voidmeineFunktion(void){
6
//wichtiges Zeug
7
}
8
9
//'meinStruct' ist ein Pointer und zeigt auf das erste 'char' (101) im Speicherfeld.
10
typedefstruct{
11
charelementA;
12
charelementB;
13
charelementC;
14
}mStruct;
15
16
constmStructmeinStruct[]={
17
{101,102,103},
18
{201,202,203},
19
{301,302,303}
20
};
Das glaub ich alles nicht!
Sind folgende aussagen nun Richtig?
- 'meinCharArray' ist eine Variable die das erste 'char' repräsentiert.
RICHTIG??
- 'meineFunktion' ist ebenfalls 'nur' eine Variable. RICHTIG??
- 'meinStruct' ist ebenso nur eine Variabel die den Wert '101'
repräsentiert. RICHTIG??
Ich weiß das sind sehr grundlegende Fragen aber ich weiß zur Zeit
einfach nicht mehr wo oben und unten ist.
Danke!
> Sind folgende aussagen nun Richtig?> - 'meinCharArray' ist eine Variable die das erste 'char' repräsentiert.> RICHTIG??
Nein.
> - 'meineFunktion' ist ebenfalls 'nur' eine Variable. RICHTIG??
Nein.
> - 'meinStruct' ist ebenso nur eine Variabel die den Wert '101'> repräsentiert. RICHTIG??
Nein.
Besorg' Dir ein C-Buch. Nach wie vor empfehlenswert: Brian Kernighan &
Dennis Ritchie, "Programmieren in C", zweite Ausgabe, Hanser-Verlag.
stift schrieb:> Folgendes glaubte ich zu wissen:>> // 'meinCharArray' ist ein Pointer und zeigt auf das erste 'char'("a")> im Speicherfeld das 6+1 Bytes belegt.> char meinCharArray[] = "abcdef";
Nein.
Du schreibst jetzt 100 mal: Ein Array ist kein Pointer!
> // 'meineFunktion' ist ein Pointer und zeigt auf die Einsprungadresse> dieser Prozedur> void meineFunktion (void){> //wichtiges Zeug> }
auch nicht.
>> //'meinStruct' ist ein Pointer und zeigt auf das erste 'char' (101) im> Speicherfeld.> typedef struct {> char elementA;> char elementB;> char elementC;> } mStruct;>> const mStruct meinStruct[] = {
Da steht eindeutig:
meinStruct ist ein Array. Und wie du, nach 100 mal schreiben, weißt: Ein
Array ist kein Pointer.
> Das glaub ich alles nicht!
Ach, jetzt kommt die Auflösung.
-> Völlig richtig. Das sollst du auch nicht glauben!
> Sind folgende aussagen nun Richtig?> - 'meinCharArray' ist eine Variable die das erste 'char' repräsentiert.> RICHTIG??
meinCharArray ist ein Array.
Punkt.
Aber: der Variablenname eines Arrays fungiert automatisch als die
Startadresse des Arrays.
In dem Punkt unterscheiden sich "normale" Variablen und Arrays.
In
int i, j;
i = j;
fungiert j als 'Platzhalter' für den Inhalt von j.
Und das ist bei allen anderen Datentypen ebenfalls so. Mit Ausnahme von
Arrays. Bei Arrays fungiert der Variablenname als 'Platzhalter' für die
Startadresse des Arrays.
> - 'meineFunktion' ist ebenfalls 'nur' eine Variable. RICHTIG??
meineFunktion ist der Name einer Funktion. Hat mit Variablen erst mal
nichts zu tun.
Aber ich denke man könnte dir insofern beipflichten, als die Erwähnung
des Namens einer Funktion in einem Ausdruck ähnlich wie bei Arrays die
Startadresse der Funktion bezeichnet. Und auf diese Startadresse kann
man die Operation () anwenden, also die Funktion aufrufen, deren
Startadresse gerade genannt wurde.
> - 'meinStruct' ist ebenso nur eine Variabel die den Wert '101'> repräsentiert. RICHTIG??
meinStruct ist ein Array, das in diesem Fall dann eben nicht aus lauter
ints besteht sondern eben aus Strukturobjekten. Aber es ist ein Array.
Und damit gelten alle Regeln für Arrays.
Übrigens:
Was sich bei mir in der Ausbildung ganz gut bewährt hat:
Die strikte Trennung zwischen den Begriffen 'Adresse' und 'Pointer'.
Alles in einem Computer hat eine Adresse. Eine Adresse ist also nichts
anderes als ein Zahlenwert.
Ein Pointer hingegen ist eine Variable, die so eine Adresse speichern
kann.
Ja, ich weiß. In der täglichen Praxis wird gerne für alles der Begriff
Pointer benutzt. Aber ich habe die Erfahrung gemacht, dass es am Anfang
einfacher wird, wenn man sich den Unterschied klar macht und auch
sprachlich erst mal benutzt.
Da gibt es zb den Adress-Of Operator in C. Das vorangestellte &
Es liefert die Adresse (ein Zahlenwert) eines Objektes
1
intj;
2
3
&j;
&j ist einfach nur die Adresse im Speicher, an der j angelegt wurde. Und
diese Adresse kann man in einer Pointer-Variablen speichern
1
intj;
2
int*p;
3
4
p=&j;
p enthält die Adresse, unter der j im Speicher zu finden ist.
So gesehen könnte man sagen, dass bei Arrays automatisch immer ein &
vorangestellt wird. (Mit Ausnahme von sizeof natürlich. Aber sizeof ist
sowieso ein anderes Kapitel)
Karl Heinz Buchegger schrieb:> stift schrieb:>>> Folgendes glaubte ich zu wissen:>>>> // 'meinCharArray' ist ein Pointer und zeigt auf das erste 'char'("a")>> im Speicherfeld das 6+1 Bytes belegt.>> char meinCharArray[] = "abcdef";>> Nein.> Du schreibst jetzt 100 mal: Ein Array ist kein Pointer!
Es kann aber in ganz vielen Situationen so verwendet werden!
>> // 'meineFunktion' ist ein Pointer und zeigt auf die Einsprungadresse>> dieser Prozedur>> void meineFunktion (void){>> //wichtiges Zeug>> }>> auch nicht.
Auch meineFunktion kann wie eine Variable verwendet werden, nämlich
wie eine vom Typ void (*)(void):
1
externvoidmeineFunktion(void);
2
3
// Funktionspointer func mit meineFunktion initialisieren
4
void(*func)(void)=meineFunktion;
5
6
// Gibt einen Funktionspointer zurück
7
void(*get_func(void))(void)
8
{
9
returnmeineFunktion;
10
}
Übersetzt mit avr-gcc wird das z.B.:
1
.text
2
get_func:
3
ldi r24,lo8(gs(meineFunktion))
4
ldi r25,hi8(gs(meineFunktion))
5
ret
6
7
.data
8
func:
9
.word gs(meineFunktion)
>> //'meinStruct' ist ein Pointer und zeigt auf das erste 'char' (101) im>> Speicherfeld.>> typedef struct {>> char elementA;>> char elementB;>> char elementC;>> } mStruct;>>>> const mStruct meinStruct[] = {>> Da steht eindeutig:> meinStruct ist ein Array. Und wie du, nach 100 mal schreiben, weißt:> Ein Array ist kein Pointer.
Abermals: In vielen Situationen gilt die "Arrays decaly to a Pointers"
Regel, d.h. meinStruct kann wie ein Zeiger verwendet werden.
Johann L. schrieb:> Auch meineFunktion kann wie eine Variable verwendet werden, nämlich wie> eine vom Typ void (*)(void):
Aber nicht doch. Das ist allenfalls eine Konstante.
Versuch mal, Deiner "Variablen" irgendwas zuzuweisen.
Danke für die ausführlichen antworten!
Karl Heinz Buchegger schrieb:> > So gesehen könnte man sagen, dass bei Arrays automatisch immer ein &> > vorangestellt wird. (Mit Ausnahme von sizeof natürlich. Aber sizeof ist> > sowieso ein anderes Kapitel)
und genau das verwirrt mich total, einerseits wird das array wie ein
pointer behandelt andererseits muss man kein & oder * davor schreiben
wie bei variablen
Was ein Pointer ist ist mir total klar und verständlich!
Für mich gibt es zwei Kategorien punkto & und *
1. Variablen
2. Pointer
und ich weiß nicht wo ich Arrays einordnen soll. und das macht mich
schon ganz verrückt!
Johann L. schrieb:>> Nein.>> Du schreibst jetzt 100 mal: Ein Array ist kein Pointer!>> Es kann aber in ganz vielen Situationen so verwendet werden!
Kann er.
Aber es IST kein Pointer.
Das ist wichtig, dass man da gerade bei Anfängern nicht zu lasch ist.
Nur weil etwas in der Verwendung wie ein Pointer behandelt werden kann,
bedeutet das nicht das es einer ist. Denn sonst versteht ein Anfänger
nie, worin das Problem in
stift schrieb:> und genau das verwirrt mich total, einerseits wird das array wie ein> pointer behandelt
Diese Aussage ist gefährlich!
> andererseits muss man kein & oder * davor schreiben> wie bei variablen
Weil bei anderen Variablen der Name der Variable für den Inhalt der
Variablen steht, während bei Arrays der Name des Arrays für die
Startadresse des Arrays steht (mit Ausnahme von sizeof). Alles andere
folgt daraus.
Und das wars auch schon. Das ist im Grunde der ganze Unterschied in der
Verwendung den es zwischen Arrays und anderen Datentypen gibt.
> Für mich gibt es zwei Kategorien punkto & und *> 1. Variablen> 2. Pointer
Wo soll da ein Unterschied sein? Pointer-Variablen sind auch nichts
anderes als ganz normale Variablen. Sie haben eine Adresse, unter der
sie im Speicher abgelegt werden und sie haben einen Inhalt, der
seinerseits wieder eine Adresse ist (und als solche selbstverständlich
mit einem * dereferenziert werden kann.)
Karl Heinz Buchegger schrieb:>> Für mich gibt es zwei Kategorien punkto & und *>> 1. Variablen>> 2. Pointer>> Wo soll da ein Unterschied sein? Pointer-Variablen sind auch nichts> anderes als ganz normale Variablen. Sie haben eine Adresse, unter der> sie im Speicher abgelegt werden und sie haben einen Inhalt, der> seinerseits wieder eine Adresse ist (und als solche selbstverständlich> mit einem * dereferenziert werden kann.)
Ja genau so seh ich das auch!
Ich meinte damit:
will ich an die Adresse eine Variable pack ich das '&' davor,
will ich an die Adresse die im Pointer hinterlegt ist brauch ich gar
nichts.
will ich an den Wert einer Variable brauch ich nichts weiter dazu.
will ich an den Wert der Adresse die im Pointer hinterlegt ist nehm ich
mir das helferlein '*' mit dazu.
Das ist mir alles 1A klar!
Nur wann brauch ich welche operatorn bei Arrays?
stift schrieb:> will ich an den Wert einer Variable brauch ich nichts weiter dazu.
Exakt.
> will ich an den Wert der Adresse die im Pointer hinterlegt ist nehm ich> mir das helferlein '*' mit dazu.
Das sind aber 2 Operationen
Die erste Operation ist: welcher Wert ist in der Variablen (dem Pointer)
gespeichert.
Um an diesen Wert zu kommen, schreibst du einfach den Namen der
Variablen hin.
Als Ergebnis erhältst du eine Adresse.
Und erst mit dieser Adresse machst du dann die 2.te Operation: Nämlich
diese Adresse dereferenzieren.
Ich denke hier liegt dein geistiges Problem, dass du
1
*Ptr
als eine Aktion ansiehst. Nope: es sind deren 2
Es ist in einem gewissen sinne gleichwertig wie wenn du schreiben
würdest
1
inti,j;
2
3
i=-j;
das - modifiziert in keinster Weise, was durch die Erwähnung von 'j'
ausgelöst wird. Die Erwähnung von j löst immer noch genau das gleiche
aus wie sonst auch: Der Wert von j wird geholt.
Und dann wird als 2. Operation auf diesen Wert die Operation 'Negieren'
angewendet. Nichts anderes passier hier
1
inti;
2
int*ptr;
3
4
i=*ptr;
Die Verwendung der Variablen 'ptr' sorgt dafür, dass ihr Wert besorgt
wird. Nur ist der Wert, der in einer Pointer Variablen gespeichert ist,
selber wieder eine Speicheradresse, die durch den Zugriff auf 'ptr'
geliefert wird. Und genau wie in der Negation wird dann auf diesen Wert
(die gelieferte Adresse) eine weitere Operation, nämlich die Operation *
angewendet. Was auch soweit klappt, denn der *-Operator will genau das:
eine Adresse. Der *-Operator sieht dann an dieser Stelle im Speicher
nach und besorgt dann seinerseits den Wert, der an dieser Adresse im
Speicher gespeichert ist.
> Das ist mir alles 1A klar!> Nur wann brauch ich welche operatorn bei Arrays?
Die Operationen sind nach wie vor dieselben. Der einzige Unterschied
besteht einfach nur darin, dass die Verwendung des Variablennamens des
Arrays eben nicht den Wert liefert, der in dieser Variablen gespeichert
ist, sondern die Adresse an der dieses Array im Speicher liegt.
Ich weiß nicht, wie ich das anders oder besser ausdrücken könnte.
Vielleicht hat sich ja jetzt dein Knopf gelöst, dadurch dass du *ptr
nicht mehr als eine integrale Einheit ansiehst sondern als 2
Operationen:
1
* Wert holen
2
* auf diesen Wert eine Operation anwenden
Ok, eine Variante probier ich noch
gegeben
1
inti;
2
int*ptr;
und die Aufgabe lautet, die Adresse der Variablen i in ptr zu speichern.
Da die Verwendung des Variablennamens 'i' den Inhalt der Variablen (den
Wert, also zb 5 oder 8 oder 42) benennen würde, musst du den Adress-Of
Operator benutzen um die Bedeutung von 'i' zu verändern.
1
ptr=&i;
dieser Adress-Of Operator modifiziert also den darauffolgenden Ausdruck
dahingehend, dass nicht mehr der Wert des Ausdrucks geliefert wird,
sondern die Adresse im Speicher, an der der Ausdruck selber (in diesem
Fall eben das i) zu finden ist. Natürlich nur, sofern es für diesen
Ausdruck überhaupt eine Speicheradresse gibt.
Der & Operator besorgt diese Adresse und da ptr als Pointer Variable
eine Variable ist, die Adressen speichern kann, kann ich diese Adresse
ptr zuweisen. Auch hier wieder: da ptr kein Array ist, ist mit der
Benennung der Variablen automatisch der Wert gemeint, der in dieser
Variablen gespeichert ist. D.h. in die Variable ptr wird als Wert die
Adresse gespeichert, die sich aus dem Adress-Of Operator ergibt.
Jetzt im Gegenzug dazu
1
inta[5];
2
int*ptr;
nur gestatte mir das Pferd jetzt von hinten aufzuzäumen. Wie müsste man
es schreiben, wenn man die Startadresse des Arrays in ptr speichern
will.
Nun, links vom = muss auf jeden Fall stehen
1
ptr=
soweit ist das klar, denn hier wird ja wieder der Wert angesprochen, der
in der Variablen ptr gespeichert wird. Aber wie kriegt man diesen Wert?
Eines ist klar: Es muss eine Adresse sein, denn nur Adressen können in
Pointer Variablen abgelegt werden. Aber wie lautet das in diesem Fall?
In obigen Fall lautete es
1
ptr=&i;
weil ja i alleine den Wert von i bezeichnen würde.
Hier haben wir es aber mit einem Array zu tun. Und bei einem Array steht
der Name des Arrays in der Verwendung automatisch für die Startadresse
des Arrays im Speicher. Das heißt
1
a
alleine ist bereits eine Adresse! Denn a ist ja ein Array.
Das heisst aber auch, ich brauche an der rechten Seite gar nichts tun.
Es ist kein & Operator notwendig um eine Adresse zu erhalten. Denn 'a'
alleine ist ja bereits diese Adresse
1
ptr=a;
ist daher völlig korrekt.
Jetzt machen wir was anderes. Wertzuweisung
Wieder
1
inti,j;
Wie weist du den Wert von j an i zu? Du schreibst
1
i=j;
Dies deshalb, weil ja j alleine bereits für den Wert steht, der in j
gespeichert ist.
Nächster Fall. Das j ersetzt wir durch einen Pointer
1
inti;
2
int*ptr;
wie weist du den Wert, auf den ptr zeigt, an i zu?
1
i=ptr;
kann nicht die Lösung sein. Denn die Verwendung von ptr alleine liefert
ja erst die Adresse unter der der Wert zu finden ist. Diese Adresse
wollen wir aber nicht, sondern wir wollen den Wert dahinter. Also muss
eine Dereferenzierung her
1
i=*ptr;
der * Operator macht genau das. Er wertet den Ausdruck rechts von sich
aus, das muss dann eine Adresse sein und genau unter dieser Adresse
sieht er im Speicher nach und holt den zugehörigen Wert. Und der wird
dann an i zugewiesen.
Wie wäre das bei einem Array?
1
inti;
2
inta[5];
Da die Benutzung von a nicht den Wert in a liefert, sondern seine
Startadresse, müsste es doch möglich sein, zu schreiben
1
i=*a;
denn alles was der * Operator haben will ist eine Adresse, die er
derefernziert. Der Ausdruck a liefert so eine Adresse.
Und in der Tat: Ja, genau das ist möglich! Und da a für die Startadresse
des Arrays steht, liefert *a genau den allerersten Wert im Array (den
der 'am Start' steht)
Wie kriegt man den nächsten Wert im Array?
Hier kommt Pointer Arithmetik ins Spiel, aber nur soviel
1
i=*(a+1);
(das '+ 1' erklärt sich aus der Pointer Arithmetik)
Und genau das ist dann auch die eigentliche Schreibweise, wie man auf
das zweite Element im Array zugreift. Die ausführliche Schreibweise für
das erste Array Element wäre
1
i=*(a+0);
Und hier sieht man auch, dass der Dereferenzier-* einfach nur eine
Operation ist, die eine Adresse in den Wert an dieser Adresse umsetzt.
Genauso wie zb bei einem Negier-Minus muss ich einen Ausdruck der zuvor
ausgewertet werden soll (in diesem Fall eben die Adress-Arithmetik) in
Klammern schreiben.
Da man allerdings derartige Array-Operationen häufig braucht, ist diese
Schreibweise um an den Wert an einer bestimmten Array-Position zu
kommen, äusserst mühsam. Daher haben die Macher von C dafür eine andere
Schreibweise definiert. Sie haben definiert, dass die Schreibweise
1
*(a+i)
gleichwertig sein soll zur Schreibweise
1
a[i]
Um an das zweite Array-Element zu kommen mussten wir schreiben
1
i=*(a+1);
Aufgrund der zusätzlichen Definition ergibt sich, dass es eine
alternative Schreibweise gibt. Nämlich
1
i=a[1];
aber das ist im Grunde nur eine alternative Schreibweise. (Und warum die
besser ist, brauchen wir hoffentlich nicht zu diskutieren :-) Auch hier
gibt es den Dereferenzier-* nach wie vor, der notwendig ist um an einen
Wert zu kommen da ja die Nennung eines Arraynamens die Startadresse des
Arrays bezeichnet. Nur ist er in dieser Schreibweisendefinition
versteckt.
Johann L. schrieb:> Abermals: In vielen Situationen gilt die "Arrays decaly to a Pointers"> Regel, d.h. meinStruct kann wie ein Zeiger verwendet werden.
Für alle, die wie ich sonst erst einmal googlen müssen:
arrays decay into pointers.
Ein Array ist eine Reihe von Elementen im RAM.
Der Name des Arrays wird vom Compiler als Adresse des ersten Elements
referenziert. Daher können die Zugriffsoperatoren wie bei Pointern
verwendet werden (*name = foo; foo = name[1];).
Der Name des Arrays ist aber kein Pointer!
Er ist nur eine konstante Zahl.
Ein Pointer ist eine Variable im RAM, die als Adresse auf irgendwas
referenziert wird.
Vor jeglicher Verwendung muß also dem Pointer eine reale Adresse einer
Variablen oder Funktion zugewiesen werden!
Peter Dannegger schrieb:> Der Name des Arrays ist aber kein Pointer!> Er ist nur eine konstante Zahl.
Er ist was den Typ angeht ein Pointer. Der Wert kann während der
Ausführung des Programms konstant sein, wenn statisch, oder auch bloss
im Verlauf der Ausführung einer Funktion konstant sein.
> Ein Pointer ist eine Variable im RAM, die als Adresse auf irgendwas> referenziert wird.
Der formale Begriff "Pointer" stammt aus dem Kontext der Datentypen. Ob
er sich auf eine Variable bezieht ist offen.
A. K. schrieb:> Der Wert kann während der Ausführung des Programms konstant sein,> wenn statisch, oder auch bloss im Verlauf der Ausführung einer> Funktion konstant sein.
Anders formuliert:
Der Wert ist während der Gültigkeits- oder Lebensdauer des Arrays
konstant; eine Zuweisung/Änderung ist nicht möglich.
Denn "kann konstant sein", was man aus Deiner Formulierung herauslesen
kann, impliziert, daß das nicht so sein müsste.
Das hast Du zwar nicht gemeint, das "kann" bezieht sich bei Dir nicht
auf die Konstanz, sondern die Lebensdauer des Arrays (während der
Ausführung des Programmes oder während der Ausführung einer Funktion) -
aber das ist meiner Ansicht nach in Deiner Formulierung nicht so gut zu
erkennen.
Rufus Τ. Firefly schrieb:> Der Wert ist während der Gültigkeits- oder Lebensdauer des Arrays> konstant; eine Zuweisung/Änderung ist nicht möglich.
Ja, so ist das besser ausgedrückt.
Karl Heinz Buchegger schrieb:> Weil bei anderen Variablen der Name der Variable für den Inhalt der> Variablen steht, während bei Arrays der Name des Arrays für die> Startadresse des Arrays steht (mit Ausnahme von sizeof).
Gerade weil Arrays als Operand des sizeof-Operators anders behandelt
werden als in gewöhnlichen Ausdrücken, kann man durch Experiemnte mit
sizeof einiges über die Unterschiede zwischen Arrays, Strukturen und
Pointer lernen.
In folgendem Beispiel gibt das PRINTSIZE-Makro die mit sizeof ermittelte
Größe des Arguments aus:
@stift:
Decke in folgender Ausgabe die ganz rechte Spalte mit den Ergebnissen ab
(real mit einem Stück Papier oder virtuell mit einem anderen Fenster auf
dem Bildschirm. Dann versuche die Ergebnisse zu erraten. Wenn du bei
allen Beispielen (bis auf die nicht C99-konformen ganz unten) richtig
liegst, hast du die Unterschiede kapiert :)
Hinweis: die Größe eines Pointers auf einem PC ist bei einem 32-Bit-OS 4
Bytes, bei einem 64-Bit-OS 8 Bytes. Die untige Ausgabe stammt von einem
64-Bit-OS.
1
sizeof (a1) = 100
2
sizeof (a1[0]) = 1
3
sizeof (*a1) = 1
4
sizeof (a1+0) = 8
5
6
sizeof (a1_arg_ptr) = 8
7
sizeof (a1_arg) = 8
8
sizeof (a1_arg_100) = 8
9
sizeof (*a1_arg_ptr) = 1
10
11
sizeof (a2_arg_ptr) = 8
12
sizeof (a2_arg_7) = 8
13
sizeof (a2_arg_6_7) = 8
14
sizeof (*a2_arg_ptr) = 7
15
16
sizeof (s1_arg_ptr) = 8
17
sizeof (s1_arg) = 3
18
19
sizeof (a2) = 42
20
sizeof (a2[0]) = 7
21
sizeof (a2[0]+0) = 8
22
sizeof (*a2[0]) = 1
23
sizeof ((*a2)[0]) = 1
24
sizeof (a2[0][0]) = 1
25
sizeof (*a2) = 7
26
sizeof (**a2) = 1
27
sizeof (a2+0) = 8
28
sizeof ((a2+0)[0]) = 7
29
sizeof (*(a2+0)) = 7
30
31
sizeof (a3) = 8
32
sizeof (a3[0]) = 800
33
sizeof (a3[0][0]) = 8
34
sizeof (a3[0][0][0]) = 1
35
36
sizeof (s1) = 3
37
sizeof (s1) = 3
38
39
sizeof (s2) = 8
40
sizeof (s2.i1) = 4
41
sizeof (s2.c1) = 1
42
43
sizeof (as1) = 30
44
sizeof (as1[0]) = 3
45
sizeof (*as1) = 3
46
47
sizeof (&function) = 8
48
sizeof (&*function) = 8
49
sizeof (&****function) = 8
50
51
sizeof von und Zeigerarithmetik mit
52
Funktionen ist nicht C99-konform,
53
aber als GNU-Erweiterung möglich:
54
55
sizeof (function) = 1
56
sizeof (*function) = 1
57
sizeof (****function) = 1
58
sizeof (function+0) = 8
Du kannst dir natürlich nach Belieben weitere verrückte
Array-pointer-Ausdrücke ausdenken und ebenfalls mit dem Programm testen.
Sehr genial wie ausführlich du das hier erklärst!
So ist es genau perfekt wenn man festgefahren ist.
zudem hab ich mir jetzt auch noch ein eBook von amazon besorgt "The C
Programming Language" in englischer orginalfassung und hab das kapitel
array, pointer und strukturen durchgearbeitet.
Jetzt darf ich behaupten das mein wissen auf einen anderen level ist als
vor ein paar tagen noch war. Nun muss ich es nur mehr umsetzten lernen.
Ehrlich gesagt is es mir lieber viel zu lernen wie man diese zeichen "*
& -> . ++()--" in der richtigen kompination einsetzt als wenn man
hiermit eingeschränkt wäre. Hat man das erst kapiert kann man echt
("krankes" || "geniales") zeug mit struct-arrays anstellen.
Das was mich vor tagen hauptsächlich verwirrt hat war das 'versteckte'
*-chen hinter diesen [] klammern.
Herzlichsten Dank für eure tolle Hilfe und auch für den Buchtip.
ps: die nächste hürde wird werden die ganzen stract-arrays mit seine
vielen funktions-,pointern im flash zu lagern. Aber da meld ich mich
gesondert sollt ich hierbei in ne dunkle sackgasse geraten.
stift schrieb:> Das was mich vor tagen hauptsächlich verwirrt hat war das 'versteckte'> *-chen hinter diesen [] klammern.
Ja, dieser Pointer - Array Zusammenhang ist am Anfang ein wenig
gewöhnungsbedürftig und kommt einem seltsam vor. Aber eigentlich ist er
ganz einfach.
Wenn man verinnerlicht hat, dass derartige Array-Index Zugriffe nichts
anderes als versteckte Pointer-Arithmetik ist, dann erklärt sich so
manches.
Übrigens ist das nicht nur reine Theorie. Ein C Compiler muss einen
Ausdruck
1
a[i]
tatsächlich zuallererst in die Pointer Form bringen
1
*(a+i)
ehe er dann damit weitermachen kann.
Das trägt dann zu einer Stilblüte bei, die es so nur in C gibt. Denn a +
i ist ja gleichwertig zu i + a
D.h. das zum Beispiel der Ausdruck
1
*(a+2)
identisch ist zum Ausdruck
1
*(2+a)
Du wirst jetzt sagen: no - na.
Aber die Sache geht weiter. Denn daraus und aus der Tatsache, dass []
Operationen sofort in Pointer Arithmetik umgewandelt werden muss, folgt
auch, dass in C
1
a[2]
und
1
2[a]
in allen Belangen gleichwertig sind. Und letzters sieht dann schon ein
wenig seltsam aus. Ist aber völlig legal! (Wenn auch schlechter Stil
:-))
Ausdruck 1, 2, 3 haben das gleiche Ergebnis, nur Ausdruck 4 nicht.
x ist ein Array, d.h. die Konstante: Adresse des 'H'.
Dann kann &x nicht berechnet werden, da diese Zahl nirgends gespeichert
ist.
Und dann macht der Compiler: &x = x
Deshalb liefert Ausdruck 2 das gleiche Ergebnis wie Ausdruck 1.
y ist aber ein Pointer und daher hat er auch eine Adresse. Ausdruck 4
gibt also nicht 'H' aus, sondern das low-Byte der Adresse von y.
Wer aber immer schön brav die Warnungen liest, kriegt auch ein:
test.c:13: warning: assignment from incompatible pointer type
test.c:19: warning: assignment from incompatible pointer type
Peter Dannegger schrieb:> Dann kann &x nicht berechnet werden, da diese Zahl nirgends gespeichert> ist.
Vorsicht, jetzt hast du dich selber bei der Unterscheidung zwischen
Array und Pointer verheddert.
Der Ausdruck &x ist mitnichten ein Pointer auf eine Zahl bzw. einen
anderen Pointer, sondern ein Pointer auf ein Array, was einen großen
Unterschied macht.
Die drei wichtigsten Regeln im Zusammenhang mit Arrays und Pointern
lauten:
1
Regel 1:
2
3
Wenn dem Array-Namen ein sizeof oder der Adressoperator (&) vorangeht,
4
steht der Name für das gesamte Array, sonst wird er als Pointer auf das
5
erste Array-Element interpretiert.
6
7
Regel 2:
8
9
Der Ausdruck a[i] ist gleichbedeutend mit *(a+i). Folglich ist a[0] das
10
Gleiche wie *a.
11
12
Regel 3:
13
14
Das Ergebnis des Ausdrucks a+i ist ein Pointer, dessen Zahlenwert der
15
Zahlenwert von a plus das i-fache der Byte-Größe des Objekts, auf das a
16
zeigt, ist. Formal:
17
18
#(a+i) = #a + i * sizeof (*a)
19
20
#p sei dabei der Zahlenwert des Pointers p.
In &x ist x also kein Pointer, sondern das Array selbst. Wird der
Adressoperator auf ein Array angewendet werden, liefert er als Ergebnis
logischerweise einen Pointer auf das Array. Wohlbemerkt auf das
gesamte Array und nicht etwa nur auf dessen erstes Element! Beide
haben zwar den gleichen Zahlenwert, aber nicht den gleichen Typ: Der
Pointer auf das erste Element ist vom Typ (char *), während der Pointer
auf das Array vom Typ ((char [12]) *) ist.
Das folgende, etwas modifizierte "Hallo Peter"-Programm macht den
Unterschied deutlich:
1
#include<stdio.h>
2
3
charx[]="Hallo Peter";
4
5
intmain(void){
6
7
printf("%p\n",x);
8
9
printf("%c\n",x[0]);
10
printf("%c\n",x[1]);
11
12
printf("%c\n",(&x)[0]);
13
printf("%c\n",(&x)[1]);
14
15
return0;
16
}
Ausgabe (auf meinem PC, die Zeilen 1, 4 und 5 werden bei euch
wahrscheinlich anders aussehen):
1
0x600940
2
H
3
a
4
@
5
L
0x600940 ist die Adresse von x, und 'H' und 'a' sind die ersten beiden
Array-Elemente von x. Bis hierher ist das noch leicht nachvollziehbar.
Aber woher kommen das '@' und das 'L'?
Nach Regel 1 ist &x ein Pointer auf das Array. Durch Anhängen von [0]
wird dieser Pointer dereferenziert, das Ergebnis ist also das Array
selbst. Da vor dem Array-Ausdruck (&x)[0] weder ein sizeof noch der
Adressoperator steht, wird er nach Regel 1 als Pointer auf das erste
Element interpretiert. Und dieser Pointer hat den Zahlenwert 0x600940.
Die printf-Funktion mit dem "%c"-Format gibt davon die niederwertigsten
8 Bits (0x40) als ASCII-Zeichen ('@') aus.
Der Ausdruck (&x)[1] in der nächsten Zeile ist nach Regel 2
gleichbedeutend mit *(&x+1). &x ist dabei nach Regel 1 wieder ein
Pointer auf das gesamte Array. Die Pointer-Addition +1 addiert nach
Regel 3 zum Zahlenwert des Pointers die Byte-Größe des Arrays. Diese ist
12, also wird 12 zur Adresse 0x600940 addiert, das Ergebnis ist
0x60094C. Von diesem Wert zeigt printf wieder die niederwertigsten 8
Bits als ASCII-Zeichen an, und das ist ein 'L'.
Wie man sieht, ist das mit den Arrays und Pointern ganz einfach, wenn
man nur das Regelwerk für die Umwandlung zwischen den beiden im Kopf
hat.
Karl Heinz Buchegger@
>Alles in einem Computer hat eine Adresse. Eine Adresse ist also nichts>anderes als ein Zahlenwert.
Korrekt!
>Ein Pointer hingegen ist eine Variable, die so eine Adresse speichern>kann.
Ist aber auch nur ein Zahlenwert als Adresse!
Ein Pointer ist (je nach native datatype) erreichbar durch eine Adresse,
deren Inhalt eine Speicheradresse enthält!
>meinCharArray ist ein Array.>Punkt.
meinCharArray repräsentiert den Anfang eines Arrays. Das Array selber
steht fein sauber im Speicher!
>In dem Punkt unterscheiden sich "normale" Variablen und Arrays.
auch nicht. Beides sind Variablen. Die eine Speicherstelle reräsentiert
den Wert der Variablen, die andere die Speicheradresse des Arrays.
ich höre hier auf
Rosa
Rosa-Kleidchen schrieb:>>In dem Punkt unterscheiden sich "normale" Variablen und Arrays.> auch nicht. Beides sind Variablen. Die eine Speicherstelle reräsentiert> den Wert der Variablen, die andere die Speicheradresse des Arrays.>> ich höre hier auf
Ist auch besser so. Du beginnst dich hier gerade in einen Wirbel
reinzureden.
>Ist auch besser so. Du beginnst dich hier gerade in einen Wirbel>reinzureden.
Ich beginne mich nicht in einen Wirbel hinein zureden. Meine
Ausführungen sind kurz und knapp im Gegesatz zu deinen "Reden"!
Rosa
Rosa-Kleidchen schrieb:>> Ist auch besser so. Du beginnst dich hier gerade in einen Wirbel>>>reinzureden.> Ich beginne mich nicht in einen Wirbel hinein zureden. Meine> Ausführungen sind kurz und knapp im Gegesatz zu deinen "Reden"!
Dafür aber ungenau bis falsch, spätestens hier:
Rosa-Kleidchen schrieb:> meinCharArray repräsentiert den Anfang eines Arrays.
Eben nicht immer. Du solltest dir vielleicht doch mal die "Reden" in
diesem Thread durchlesen. Oder ein C-Buch.
>Eben nicht immer. Du solltest dir vielleicht doch mal die "Reden" in>diesem Thread durchlesen. Oder ein C-Buch.
a.) Begründung für "eben nicht immer"???
b.) Guck dir das an, was im Speicher steht. Adressen und Werte. Mehr
nicht!
Dann versteht man ziemlich schnell, wie sich das mit Pointer, Adressen,
Variablen,.. verhält.
Rosa
Ps: K&R ist ein alter Hut...
Rosa-Kleidchen schrieb:> a.) Begründung für "eben nicht immer"???
Steht zwar schon oben, aber nochmal für dich: Der Unterschied wird dann
sichbar, wenn du das Ergebnis von sizeof(meinCharArray) anschaust.
Rosa-Kleidchen schrieb:> Ps: K&R ist ein alter Hut...
was nichts daran ändert, daß alles, was da drin steht, unverändert
richtig ist.
Oliver
>Steht zwar schon oben, aber nochmal für dich: Der Unterschied wird dann>sichbar, wenn du das Ergebnis von sizeof(meinCharArray) anschaust.
Nun gut!
nun ja, aber
char *myptr = null;
*myptr = &meinCharArray[0];
im Prinzip gibt es jetzt zwei Addressen im Speicher, die beide die
Addresse des Arrays enthalten (so ungefähr):
myptr: 0x00001111: 0x00002222 ..
meinCharArray: 0x00001120: 0x00002222 ..
Array: 0x00002222: 0x00000000 0x00000001 0x00000002
oder?
Rosa-Kleidchen schrieb:>>Steht zwar schon oben, aber nochmal für dich: Der Unterschied wird dann>>sichbar, wenn du das Ergebnis von sizeof(meinCharArray) anschaust.> Nun gut!>> nun ja, aber>> char *myptr = null;> *myptr = &meinCharArray[0];>> im Prinzip gibt es jetzt zwei Addressen im Speicher, die beide die> Addresse des Arrays enthalten
Äh, nein.
Nimm den * bei
* myptr = ....
weg und dann stimmts.
> myptr: 0x00001111: 0x00002222 ..> meinCharArray: 0x00001120: 0x00002222 ..
Auch nicht.
meinCharArray ist keine Pointer Variable.
Mir scheint du solltest dir wirklich noch mal durchlesen was ich da
weiter oben geschrieben habe anstatt da mit Halbwissen zu brillieren.
Ich schreib meine Artikel nämlich nicht umsonst relativ ausführlich,
wenn ich denke, dass es das Thema verdient hat und wichtig ist.
> oder?
Genau: oder!
>Auch nicht.>meinCharArray ist keine Pointer Variable.
nu erzähl mir mal, wie meinCharArray im Speicher steht und wie der
Inhalt aussieht.
>Mir scheint du solltest dir wirklich noch mal durchlesen was ich da>weiter oben geschrieben habe anstatt da mit Halbwissen zu brillieren.>Ich schreib meine Artikel nämlich nicht umsonst relativ ausführlich,>wenn ich denke, dass es das Thema verdient hat und wichtig ist.
Zuerst einmal brilliere ich nicht. Anzeichen sind "so ist das und fertig
und schau dir mal meine Ausführungen an"! Du solltest die Posts auch
nicht persönlich nehmen und sachlich bleiben. Das zeugt von
Sozialkompetenz.
Ich habe auch den Eindruck, das ich an deinem Verständnis vorbei rede
und du mich nicht verstehtst. Wie dem auch sei...
Rosa
Rosa-Kleidchen schrieb:> Ich habe auch den Eindruck, das ich an deinem Verständnis vorbei rede> und du mich nicht verstehtst.
Och.
Ich versteh sehr viel. Und meistens hol ich mir die relevanten
INformationen auch dann noch aus den Postings raus, wenn mein Gegenüber
das gar nicht beabsichtigt hat.
Und bei dir versteh ich, dass du den Zusammenhang Pointer-Variablen,
Adressen und Arrays noch nicht komplett richtig verstanden hast. Es
gibt schlicht und ergreifend bei
char meinCharArray[] = "abcdef";
keine Stelle im Speicher an der die Adresse der Daten abgespeichert sein
würde. Das hier
> meinCharArray: 0x00001120: 0x00002222 ..> Array: 0x00002222: 0x00000000 0x00000001 0x00000002
ist Unsinn.
meinCharArray ist keine Pointervariable, in der die Adresse der
eigentlichen Daten abgelegt wäre.
> Wie dem auch sei...
Genau.
In diesem Sinne.
Rosa-Kleidchen schrieb:> nu erzähl mir mal, wie meinCharArray im Speicher steht und wie der> Inhalt aussieht.
Speziell für dich:
Die beiden Variablen seien wie folgt definiert:
1
charmeinCharArray[]="abcdef";
2
char*myptr=meinCharArray;
Angenommen, der verwendete Prozessor hat 16-Bit-Adressen, legt Daten im
Speicher little-endian ab, meinCharArray liegt an der Adresse 0x1234,
und myptr liegt an der Adresse 0x3456. Dann sieht das Speicherbbild
der Variablen folgendermaßen aus:
1
meinCharArray:
2
3
Adresse Inhalt Erläuterung
4
5
| .... |
6
|––––––|
7
0x1234 | 0x61 | 'a'
8
|––––––|
9
0x1235 | 0x62 | 'b'
10
|––––––|
11
0x1236 | 0x63 | 'c'
12
|––––––|
13
0x1237 | 0x64 | 'd'
14
|––––––|
15
0x1238 | 0x65 | 'e'
16
|––––––|
17
0x1239 | 0x66 | 'f'
18
|––––––|
19
0x123A | 0x00 | '\0'
20
|––––––|
21
| .... |
22
23
24
myptr:
25
26
Adresse Inhalt Erläuterung
27
28
| .... |
29
|––––––|
30
0x3456 | 0x34 | lowbyte(0x1234)
31
|––––––|
32
0x3457 | 0x12 | highbyte(0x1234)
33
|––––––|
34
| .... |
Entsprechend der Speicherbelegung liefert sizeof für die Variablen die
Werte 7 und 2.
Wie du siehst, taucht die Array-Startadresse 0x1234 nur als Inhalt von
myptr auf, und das auch nur, weil myptr so initialisiert worden ist.
Das Array selbst besteht im Speicherabbild nur aus seinem Inhalt, also
den 6 Buchstaben und dem String-Terminator '\0'.