Forum: Compiler & IDEs Verständnisfrage: Pointer auf Array


von Paul H. (powl)


Lesenswert?

Hi!

Ich habe ein array:
1
char array[] = { 1, 2, 3, 4, 5 };

jetzt habe ich noch einen pointer darauf:
1
char *ptr = array; // gleichbedeutend mit &array[0]

um nun auf ein element dieses array über den pointer zuzugreifen benutze 
ich folgende schreibweise:
1
ptr[2];

was mich allerdings etwas verwirrt ist, dass man hier gar keinen 
dereferenzierungsoperator benötigt.

ich hätte eher mit sowas wie
1
(*ptr)[2];

gerechnet, jedoch funktioniert das nicht. Sind die schreibweisen 
absichtlich so unterschiedlich?

lg PoWl

von holger (Gast)


Lesenswert?

>um nun auf ein element dieses array über den pointer zuzugreifen benutze
>ich folgende schreibweise:
>
>ptr[2];

Da kannste ja gleich das Array selbst nehmen.

>was mich allerdings etwas verwirrt ist, dass man hier gar keinen
>dereferenzierungsoperator benötigt.
>ich hätte eher mit sowas wie
>
>(*ptr)[2];

Versuch mal *ptr+2

von holger (Gast)


Lesenswert?

>Versuch mal *ptr+2

Nein, falsch :(

*(ptr+2)

von Stefan Salewski (Gast)


Lesenswert?

@Paul Hamacher

Ich habe mich mit der C-Notation auch nie wohl gefühlt.

C ist wie einen Plattenbausiedlung -- aus den siebziger Jahren, weit 
verbreitet, hässlich, aber erfüllt ihren Zweck, mehr oder weniger.

von Simon K. (simon) Benutzerseite


Lesenswert?

Paul Hamacher wrote:
>
1
ptr[2];
>
> was mich allerdings etwas verwirrt ist, dass man hier gar keinen
> dereferenzierungsoperator benötigt.
Wieso? Du brauchst einen Dereferenzierungsoperator. Aber nicht den 
Stern, sondern die eckigen Klammern. Die dereferenzieren genau so wie 
der Stern (nur mit bestimmten Offset). Der Stern dereferenziert immer 
nur das erste Element.

> ich hätte eher mit sowas wie
>
>
1
(*ptr)[2];
Das wären ja zwei Dereferenzierungen.
Durch "*ptr" erhälst du eine Variable vom Typ char (Durch die 
Dereferenzierung).

Und kannst du folgendes mit einer char-Variable machen?
1
char a, b;
2
b = a[2];
Nein, kannst du nicht. Also macht es doch überhaupt keinen Sinn ;)

von Paul H. (powl)


Lesenswert?

hm..


aber ich habe ja sowas:

array:

   Adresse       Wert
+-----------+-----------+
|  0x0001   +     1     +
+-----------+-----------+
|  0x0002   +     2     +
+-----------+-----------+
|  0x0003   +     3     +
+-----------+-----------+
|  0x0004   +     4     +
+-----------+-----------+
|  0x0005   +     5     +
+-----------+-----------+

dann den pointer *ptr:

+-----------+-----------+
|  0x0006   +  0x0001   +
+-----------+-----------+

ist array im grundegenommen nicht auch ein pointer auf den anfang eines 
datenfeldes oder ist ein array im sinne von C eben ein array und kein 
pointer?

auf das array greife ich per array[2] zu. Dabei wird von der 
Anfangsadresse mit einem offset von 2 auf den Wert dereferenziert also 
lande ich beim wert der speicherstelle 0x0003, also 3.

der pointer *ptr enthält als wert ja auch wieder eine adresse, nämlich 
0x0001, die anfangsadresse des arrays.

theoretisch müsste ich ja nun zweimal dereferenzieren. einmal um von der 
pointeradresse 0x0006 auf dessen Wert 0x0001 zu kommen, und dann nochmal 
um von der Adresse 0x0001 mit dem offset 2 auf den Wert der 
speicherstelle 0x0003, also 3 zu kommen.

Daher dachte ich an (*ptr)[2];

lg Powl

von gast (Gast)


Lesenswert?

Du verwirrst dich selber indem du dir den Speicheraufbau vorstellst.

Ein Array ist eigentlich ein stink normaler Pointer mit ein paar kleinen 
Unterschieden:

Er(der Pointer) braucht keinen Speicher im Ram.
Es wird für Elemente auf die gezigt wird Speicher reserviert.
Man kann nicht den Stern zum dereferenzieren benutzen.

Deshalb kannst du ja auch schreiben
1
ptr = array;
aber nicht
1
array = ptr;

Und wie Simon bereits gesagt hat ist [] bereits eine dereferenzierung.

von I_ H. (i_h)


Lesenswert?

Der Arrayoperator für x[y] ist identisch zu:

*(x+sizeof(*x)*y)

Nix Plattenbauweise, sondern völlig logisch und nachvollziehbar.

von gast (Gast)


Lesenswert?

oder nochmals anders erklärt:
Ein Array ist ein nicht manipulierbarer Pointer ;-)

von Stefan Salewski (Gast)


Lesenswert?

>Ein Array ist eigentlich ein stink normaler Pointer mit ein paar kleinen
>Unterschieden:

>Ein Array ist ein nicht manipulierbarer Pointer ;-)

Gefällt mir nicht!

Ich würde eher sagen: Ein Array ist eine Abfolge von Elementen gleichen 
Typs, die aufeinander folgend irgendwo im Speicher angeordnet sind.

von Paul H. (powl)


Lesenswert?

I_ H. wrote:
> Der Arrayoperator für x[y] ist identisch zu:
>
> *(x+sizeof(*x)*y)
>
> Nix Plattenbauweise, sondern völlig logisch und nachvollziehbar.

das ist ja auch logisch, aber hier ist x direkt ein array; mit dem 
pointer auf ein array ist es ja genau so.

wie lautet dann die schreibweise wenn b ein pointer auf einen pointer a 
ist, welcher wiederum auf das array zeigt? ist das dann auch b[y] 
wodurch ich das gleiche ergebnis wie durch a[y] oder array[y] erhalte?

von holger (Gast)


Lesenswert?

Auch wenn mich wahrscheinlich gleich einer überholt,
meine Erklärung Array für Dummys ;)

Du hast ein Array

char array[] = { 1, 2, 3, 4, 5 };

Das liegt an einer bestimmten Adresse fest im Speicher.
Es ist kein Pointer, es ist ein Datenfeld.

&array[0] oder array kannst du an einen Pointer übergeben.

Also wie oben

ptr = array;

oder

ptr = &array[0];

Ist beides gleichwertig.
ptr wird dabei mit der Startadresse des Arrays geladen.
Er enthält die Adresse des Arrays und nicht das Array selbst.

Wenn man über ptr auf das erste Element im Array zugreifen
möchte kann man a=*ptr; oder a=ptr[0]; schreiben.

Wenn man auf ein anderes Element zugreifen möchte muss man
zu ptr (wo die Startadresse des Arrays steht) einen Offset addieren.
Das geht mit a=ptr[offset]; oder mit (der besseren Version von I_H)
a = *(ptr + sizeof(Datentyp vom Array) * offset);

Der Offset ist abhängig vom Datentyp.
Deshalb muss man auch immer im Auge behalten
was passiert wenn man ptr z.B. hochzählt.

ptr++ zählt bei einem Byte als Datentyp eins hoch.
Bei int (sagen wir hier einfach 2 Byte, das gibts auch anders)
zählt er 2 höher.

von Stefan Salewski (Gast)


Lesenswert?

@Paul Hamacher
>theoretisch müsste ich ja nun zweimal dereferenzieren. einmal um von der
>pointeradresse 0x0006 auf dessen Wert 0x0001 zu kommen, und dann nochmal
>um von der Adresse 0x0001 mit dem offset 2 auf den Wert der
>speicherstelle 0x0003, also 3 zu kommen.

>Daher dachte ich an (*ptr)[2];

Du musst unterscheiden zwischen "dereferenzieren" und Offset 
hinzuaddieren:

ptr[2];

macht beides gleichzeitig. Wie  Simon K. schrieb wirkt [] als 
Dereferenzieroperator, zusätzlich wird entsprechend dem Index (hier 2) 
die Position noch um 2*Elementgröße verschoben.

von I_ H. (i_h)


Lesenswert?

Paul Hamacher wrote:
> I_ H. wrote:
>> Der Arrayoperator für x[y] ist identisch zu:
>>
>> *(x+sizeof(*x)*y)
>>
>> Nix Plattenbauweise, sondern völlig logisch und nachvollziehbar.
>
> das ist ja auch logisch, aber hier ist x direkt ein array; mit dem
> pointer auf ein array ist es ja genau so.
>
> wie lautet dann die schreibweise wenn b ein pointer auf einen pointer a
> ist, welcher wiederum auf das array zeigt? ist das dann auch b[y]
> wodurch ich das gleiche ergebnis wie durch a[y] oder array[y] erhalte?

Dann schreibst du einfach (*b)[123] statt a[123] (oder alternativ 
b[0][123], ist aber etwas unsauber). Schau dir an was du für Typen hast. 
Wenn die Elemente im Array vom Typ T sind, ist a vom Typ T* und b vom 
Typ T**. (*b) ist wieder T* und (*b)[0] ist dann T ( (**b) ist auch T 
und das erste Element im Array).

von Paul H. (powl)


Lesenswert?

D.h.

array[2] oder a[2] oder (*b)[2].

Tut mir leid also ich finde das verwirrend dass beim direkten zugriff 
auf array mittels array[2] einmal dereferenziert wird und bei einem 
zeiger, der nicht wie das array selbst auf dessen wert sondern auf 
dessen adresse zeigt, trotzdem auch nur einmal.

lg PoWl

von I_ H. (i_h)


Lesenswert?

Och mensch, es wird doch 2mal dereferenziert... *b ist das erste mal, 
und der Array Operator das 2. mal.


Arrays gibt es in C nicht als Variablentyp, ein Array wird durch einen 
Pointer beschrieben, der auf das erste Element des Arrays zeigt. Bei 
"int array[123];" wird array als eine Variable vom Typ int* deklariert.

von Paul H. (powl)


Lesenswert?

Oh, tut mir leid ich hab mich vielleicht unklar ausgedrückt. Ich meinte 
gerade array[2] und a[2] (a ist ein pointer auf array), nicht (*b)[2];

wenn array als pointer auf das erste array-element anzusehen ist, dann 
ist a also ein pointer auf einen pointer;

von I_ H. (i_h)


Lesenswert?

> wenn array als pointer auf das erste array-element anzusehen ist, dann
> ist a also ein pointer auf einen pointer;

Nein (bzw. nur wenn das Array ein Array von Pointern ist).

Ein Array ist eine Anzahl von Werten gleichen Typs (meinentwegen T), die 
im Fall von C linear im Speicher angeordnet sind. Ein Array in C wird 
repräsentiert durch einen Zeiger auf das erste Element, ist also vom Typ 
T*.

Da wird genau 1mal dereferenziert, allerdings nicht array sondern 
array+offset. Such in Wikipedia mal nach 'dereferenzieren'. Du kannst in 
C ohme Umwege ein Array nicht über den ersten Wert angeben! Nur über 
einen Zeiger auf den ersten Wert.

von Mark .. (mork)


Lesenswert?

Nur mal so als Hinweis - a = *(ptr + sizeof(Datentyp vom Array) * 
offset); ist falsch, richtig wäre a = *(ptr + offset); Das "echte" 
Offset rechnet sich der Compiler selbst aus.

von I_ H. (i_h)


Lesenswert?

Stimmt natürlich, da bin ich fälschlicherweise von einem void* Zeiger 
ausgegangen, der wird um einzelne Bytes hochgezählt.

von Sven P. (Gast)


Lesenswert?

Mal zum Mitmeißeln:
Ein "Array", oder "Vektor" oder "Datenfeld" oder nur "Feld", ganz 
allgemein, ist eine Liste aus gleichartigen Einträgen. Nicht mehr, und 
auch nicht weniger. Wenn du auf Speicher stehst:
1
         +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
2
Adresse: |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  |...
3
         +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
4
Daten:   |  #  |  a  |  b  |  c  |  d  |  e  |  #  |  #  |  #  |  #  |...
5
         +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

Ein "Zeiger" ist eine Adresse in diesem Speicher, z.B. die 3. Kannst 
auch mehrere Zeiger nehmen, die auf die gleiche Adresse "zeigen", oder 
auch nicht, wie du möchtest:
1
  Zeiger Y--------------------------------+
2
  Zeiger X--------------------+           |
3
                              V           V
4
         +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
5
Adresse: |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  |...
6
         +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
7
Daten:   |  #  |  a  |  b  |  c  |  d  |  e  |  #  |  #  |  #  |  #  |...
8
         +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

Mit den Zeigern kannst du machen, was du möchtest, das sind einfach nur 
Hausnummern.
"Dereferenzieren" heißt jetzt, an einem Haus mit einer bestimmten 
Hausnummer anzuklopfen, um an den Bewohner (also die Daten) zu kommen.

Dereferenzieren ich jetzt z.B. Y, dann klopfe ich an Haus Nummer 5 und 
ein "e" kommt raus. Genausogut kann ich an Haus Nummer X (=3) klopfen, 
dann kommt halt das "c" raus.
Ich kann aber mit den Hausnummern noch rechnen (sind ja nur Nummern). 
Z.B. kann ich die Hausnummer X um eins erhöhen, bevor ich anklopfe. Dann 
klopfe ich also an "3+1", also an Haus Nummer 4 an, da kommt dann das 
"d" raus.

So, jetzt um eine Ecke mehr. Dein Zeiger (also die Hausnummer) muss 
natürlich auch irgendwo im Speicher stehen. Also mal so:
1
                                                           "X"
2
         +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
3
Adresse: |  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  |...
4
         +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
5
Daten:   |  #  |  a  |  b  |  c  |  d  |  e  |  #  |  #  |  3  |  #  |...
6
         +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
Ich hab die Hausnummer "X" jetzt mal an Speicherstelle 8 abgelegt. Wenn 
ich jetzt im Quelltext irgendwo "X" schreibe, dann weiß der Computer 
genau, dass mit "X" die Speicherstelle 8 gemeint ist: Du musst 
unterscheiden zwischen deinem Zeiger als Nummer (als Hausnummer) zum 
Einen: Das ist ne Nummer, die - wie jede andre auch - einfach in eine 
Variable abgelegt wird. Zum Anderen aber hast du auch das, wodrauf dein 
Zeiger zeigt: Das ist das Haus, auf das der Zeiger zeigt, also dessen 
Nummer in der Variable steht.
An den Wert von "X" kommt der Computer logischerweise ran, er weiß ja, 
wo er "X" vorher abgelegt hat - dadrauf hast du als Programmierer keinen 
Einfluss. Interessant wirds dann aber, wenn der Computer nicht auf "X" 
selbst (also auf die Variable mit der Hausnummer) zugreifen soll, 
sondern auf das Haus, dessen Hausnummer in dieser Variable steht (also 
um eine Ecke weiter). Dann nämlich kommt es zu einer "Dereferenzierung".

Jetzt verstanden? (Zugegeben, einiges hab ich vereinfacht, aber das 
Prinzip kommt hin)

von Paul H. (powl)


Lesenswert?

Danke, jetzt hab ich nen schlechtes Gewissen da du dir so ne Mühe 
gemacht hast obwohl ich das Prinzip eigentlich prima verstanden habe, 
jedoch nur mit der Notation nicht so zurechtgekommen bin.

Für Die Laien kann man sowas aber ja mal als Link im Tutorial 
hinzufügen.

lg Paul

von I_ H. (i_h)


Lesenswert?

Was die Notation angeht:
1
int a;
2
int b;
3
int c;
4
int d;

Gehen wir mal davon aus, dass die Werte hintereinander im Speicher 
liegen. Dann entspricht
1
int* array1=&a

einem Array mit 4 Einträgen, genauso wie
1
int array2[4];

array1 und array2 sind exakt vom selben Typ (das const mal außen vor 
gelassen). array[1] wird dann zB. auf b dereferenziert, genauso wie 
*(array1+1).

Wäre array2 nicht ein konstanter Zeiger (dh. die Adresse auf die er 
zeigt darf nicht verändert werden, wohl aber der Inhalt der Adresse) 
könnte man auch schreiben "array2=array1".
In jedem Fall geht "array1=array2", damit zeigen dann beide Zeiger auf 
den selben Speicher. Sprich nach "array1[0]=123;" gilt "array2[0]==123".

von Simon K. (simon) Benutzerseite


Lesenswert?

I_ H. wrote:
> Gehen wir mal davon aus, dass die Werte hintereinander im Speicher
> liegen. Dann entspricht
> ...
> einem Array mit 4 Einträgen, genauso wie
Aber bitte nur, wenn diese Variablen auch im Arbeitsspeicher existieren. 
Wenn sie in Registern liegen gibt die Ganze Suppe eher Müll. Auch müssen 
diese nicht zwangsläufig hintereinander liegen. Aber zum Glück hast du 
das ja dabei geschrieben. Nicht dass sich unser Array-Anfänger sowas 
aneignet.

> array1 und array2 sind exakt vom selben Typ (das const mal außen vor
> gelassen).
Auf gar keinen Fall! array1 ist vom Typ int* und array2 ist vom Typ 
int[4]. Einfacher Beweis:
sizeof (array2) == 4 * sizeof(int)
sizeof (array1) == 1 * sizeof(int*)
Und je nach Maschine ist dies unterschiedlich.

> array[1] wird dann zB. auf b dereferenziert, genauso wie
> *(array1+1).
Ja, unter oben genannten Voraussetzungen aber nur.

> Wäre array2 nicht ein konstanter Zeiger (dh. die Adresse auf die er
> zeigt darf nicht verändert werden, wohl aber der Inhalt der Adresse)
> könnte man auch schreiben "array2=array1".
array2 ist kein Zeiger. array2 ist ein Array. array2 kann aber bei 
Bedarf in einen Zeiger implizit konvertiert werden. Der Arrayname darf 
ohne Indirektion bzw. Dereferenzierung kein LValue sein (also ihm darf 
nichts zugewiesen werden), schlicht und einfach deswegen, weil es in 
dieser Repräsentation einfach kein Pointer ist.

von I_ H. (i_h)


Lesenswert?

Du gehst mir langsam auf die Nerven. Hast du nix besseres zu tun? Kannst 
du das nicht in dem anderen Thread lassen?

von Simon K. (simon) Benutzerseite


Lesenswert?

I_ H. wrote:
> Kannst
> du das nicht in dem anderen Thread lassen?
Was denn?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.