Forum: Compiler & IDEs Zeiger auf Array?


von Michl (Gast)


Lesenswert?

Ja, ich dachte bisher ich habe Zeiger und Arrays verstanden.
Doch jetzt komm ich grad an einen Punkt wo ich an mir zweifle.

Ich habe ein lokales Array:
1
uint8_t myArray[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};

Nun will ich eine Funktion schreiben die so ein Array entgegennimmt und 
zwei Aufgaben hat:
1) irgendwas aus den Werten berechnen, das ist ja trivial
2) als Nebeneffekt: das Array inkrementieren, damit ein zweiter Call zu 
dieser Funktion ein anderes Ergebnis liefert
1
void doSomething(uint8_t *ptr);
Eine Solche Funktion erfüllt Kriterium 1). Ich kann ihr mein Array 
übergeben. Der Name des Arrays "decayed" dabei zu einem Zeiger aufs 
erste Element, also zu
1
&(myArray[0])
. doSomething() kann also auf Array-Elemente zugreifen und damit was 
berechnen.
Allerdings: eine solche Funktion kann das Array nicht "weiterzählen".
1
void doSomething(uint8_t *ptr)
2
{
3
    ...berechnung...
4
  
5
  ptr++;
6
}
macht natürlich nicht das was ich will, ptr++ zählt ja nur die lokale 
Kopie hoch, die nach dem return eh verloren ist.

Ich muss also meinen Zeiger-auf-Array by-reference übergeben, meine 
Funktion müsste also so aussehen:
1
void doSomething(uint8_t *ptr[]);

Der Aufruf geschieht dann so:
1
doSomething(&myArray);
1
void doSomething(uint8_t *ptr)
2
{
3
    ...berechnung...
4
  
5
  (*ptr)++;
6
}
So kann ich dann den Zeiger des Callers derart manipulieren dass er beim 
nächsten Aufruf auf andere Daten operiert.

Soweit die Theorie:
Unter Win7_x64 hat das Ganze einen Absturz zufolge, ich denke mal unter 
Linux hätt ich hier die Ausgabe: Segmentation Fault.

Wo liegt denn da mein Denkfehler?
Wie kann ich es erreichen dass eine Helferfunktion den lokalen Zeiger 
des Callers inkrementiert?

von Peter II (Gast)


Lesenswert?

da sollte funktionieren.

Zeige uns ein Beispiel was vollständig ist.

von Stefan (Gast)


Lesenswert?

void doSomething(uint8_t **ptr)

der Parameter der Funktion sollte auch stimmen, sonst gibts murks.

von A. H. (ah8)


Lesenswert?

myArray ist eine Konstante, die auf eine bestimmte Adresse im Speicher 
zeigt, die kannst du gar nicht hoch zählen. Mich wundert, dass &myArray 
geht; da Konstanten ja keinen Speicherplatz belegen können sie auch 
keine Adresse haben. (Es kann aber sein, dass wegen der Pointer/Array 
Äquivalenz in diesem Fall &myArray=myArray gilt, habe ich jetzt nicht 
geprüft.)

Du brauchst so etwas:
1
uint8_t *pMyArray = myArray;

Den Pointer kannst Du hoch zählen und der hat auch eine Adresse.

PS: Woher weiß Deine Funktion, wie lang das Restfeld noch ist?

von Rolf Magnus (Gast)


Lesenswert?

Michl schrieb:
> Nun will ich eine Funktion schreiben die so ein Array entgegennimmt und
> zwei Aufgaben hat:
> 1) irgendwas aus den Werten berechnen, das ist ja trivial
> 2) als Nebeneffekt: das Array inkrementieren, damit ein zweiter Call zu
> dieser Funktion ein anderes Ergebnis liefert

"das Array inkrementieren" ergibt keinen Sinn. Ein Array kann man nicht 
inkrementieren.

> Allerdings: eine solche Funktion kann das Array nicht
> "weiterzählen".void doSomething(uint8_t *ptr)
> {
>     ...berechnung...
>
>   ptr++;
> }

Ah, du möchtest also nicht ein Array, sondern einen Zeiger 
inkrementieren.

> macht natürlich nicht das was ich will, ptr++ zählt ja nur die lokale
> Kopie hoch, die nach dem return eh verloren ist.

Richtig.

> Ich muss also meinen Zeiger-auf-Array by-reference übergeben, meine
> Funktion müsste also so aussehen:
> void doSomething(uint8_t *ptr[]);

Auch richtig. Du kansnt auch schreiben:
1
void doSomething(uint8_t **ptr);
>
> Der Aufruf geschieht dann so:
> doSomething(&myArray);

> Wo liegt denn da mein Denkfehler?

myArray ist ein Array. &myArray ist ein Zeiger auf ein Array. Du mußt 
aber einen Zeiger auf einen Zeiger übergeben, nicht auf ein Array.

> Wie kann ich es erreichen dass eine Helferfunktion den lokalen Zeiger
> des Callers inkrementiert?

Es würde schon mal helfen, wenn dieser lokale Zeiger überhaupt 
existieren würde. ;-)
1
uint8_t * p = myArray;
2
doSomething(&p);

von Michl (Gast)


Lesenswert?

1
#include <stdint.h>
2
#include <stdio.h>
3
4
void doSomething(uint8_t *buf[])
5
{
6
  uint8_t local;
7
8
  local = *buf[0];
9
10
  printf("%x\n", local);
11
12
    (*buf)++;
13
}
14
15
int main(void)
16
{
17
    uint8_t myArray[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
18
19
    doSomething(&myArray);
20
    doSomething(&myArray);
21
22
    return 0;
23
}

Sorry, hier ein komplettes Beispiel.
Was ich herausgefunden habe:
Nicht das Inkrementieren verursacht den Absturz, sondern die Zeile 
local = *buf[0];
Aber wieso? Ich muss doch den Zeiger doppelt dereferenzieren, einmal 
durch den *-Operator, einmal durch [].

von Karl H. (kbuchegg)


Lesenswert?

Michl schrieb:

Definiere erst mal, was das hier

> 2) als Nebeneffekt: das Array inkrementieren

bedeuten soll.

Was soll 'das Array inkrementieren' für eine Operation sein? Was soll 
die bewirken? Beschreib das doch mal näher (ja, ja ich weiß schon. Beim 
nächsten Aufrf soll mit anderen Daten operiert werden. Aber wo sind denn 
diese anderen Daten?)

: Bearbeitet durch User
von Rolf Magnus (Gast)


Lesenswert?

Michl schrieb:
> Nicht das Inkrementieren verursacht den Absturz, sondern die Zeile
> local = *buf[0];
> Aber wieso?

Du übergibst die Adresse von myArray. Wenn du die dereferenzierst, 
kommst du auf das erste Element des Arrays. Jetzt dereferenzierst du 
nochmal, wodurch dieses erste Element (und noch ein paar folgende) als 
Adresse interpretiert wird, und dann versucht das Programm, von dieser 
Adresse zu lesen, wobei natürlich dann Blödsinn rauskommt.

von Michl (Gast)


Lesenswert?

Nachtrag:
folgender Code tut genau das was ich will, sogar ohne Warnungen:
1
#include <stdint.h>
2
#include <stdio.h>
3
4
void doSomething(uint8_t *buf[])
5
{
6
  uint8_t local;
7
8
  local = *buf[0];
9
10
  printf("%x\n", local);
11
12
    (*buf)++;
13
}
14
15
16
void help(uint8_t *buf)
17
{
18
  doSomething(&buf);
19
  doSomething(&buf);
20
}
21
22
23
int main(void)
24
{
25
    uint8_t myArray[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
26
27
    help(myArray);
28
29
    return 0;
30
}

Durch den Umweg über die help-Funktion geht in doSomething() die Info 
verloren dass da irgendwo ein Array dahinter liegt.
Ich denke mal das ist das Problem, da man, wie Rolf Magnus und KH 
Buchegger anmerkten, ein Array nicht inkrementieren kann. Der Umweg über 
help() ist ja letzendlich gleichwertig mit der von Rolf Magnus 
vorgeschlagenen Lösung, den lokalen Pointer anzulegen.

von Michl (Gast)


Lesenswert?

Karl Heinz schrieb:
> Was soll 'das Array inkrementieren' für eine Operation sein? Was soll
> die bewirken? Beschreib das doch mal näher (ja, ja ich weiß schon. Beim
> nächsten Aufrf soll mit anderen Daten operiert werden. Aber wo sind denn
> diese anderen Daten?)

Das dient dazu, aus einem Bytestrom einige Bytes herauszulesen. Immer 
Häppchenweise, und diese zu verarbeiten.

von Karl H. (kbuchegg)


Lesenswert?

Michl schrieb:

> Sorry, hier ein komplettes Beispiel.

Dieses Beispiel ergibt keinen Sinn.

Das hier
1
void doSomething(uint8_t *buf[])
besagt, dass buf ein Array von Pointer ist, wobei jeder Pointer auf 
(mindestens) einen uint8_t zeigt.

Hier hast du also vereinbart, dass buf diese Gestalt hat
1
  buf
2
  +------+                    +---+---+---+---+---+
3
  |  o----------------------->| 3 | 8 | 5 | 2 | 4 |
4
  +------+                    +---+---+---+---+---+
5
  |  o------------+
6
  +------+        |    +---+---+
7
  |  o-------+    +--->| 2 | 8 |
8
  +------+   |         +---+---+
9
  |  o-----+ |
10
  +------+ | |
11
           | +-----> ....
12
           |
13
         ....

Nur: Deine Daten sehen nicht so aus!
Das hier
1
    uint8_t myArray[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
ist etwas völlig anderes und auch wenn du mittels eines Adressoperators 
erst mal alles ruhig gestellt hast, ändert das nichts an der Tatsache, 
dass deine Daten nicht den Aufbau haben, den die Funktion voraussetzt.
Du kannst eine Kuh weiß anstreichen, aber das macht sie nicht zum 
Schimmel.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Michl schrieb:
> Karl Heinz schrieb:
>> Was soll 'das Array inkrementieren' für eine Operation sein? Was soll
>> die bewirken? Beschreib das doch mal näher (ja, ja ich weiß schon. Beim
>> nächsten Aufrf soll mit anderen Daten operiert werden. Aber wo sind denn
>> diese anderen Daten?)
>
> Das dient dazu, aus einem Bytestrom einige Bytes herauszulesen. Immer
> Häppchenweise, und diese zu verarbeiten.

Schön.
Aber wo sind diese Daten in deinem Programm.
Wenn du sagst, du willst ein anderes Array benutzen (und das tust du mit 
deinem Code), dann muss es auch ein anderes Array geben. Wo ist dieses?

von A. H. (ah8)


Lesenswert?

Michl schrieb:
> Was ich herausgefunden habe:
> Nicht das Inkrementieren verursacht den Absturz, sondern die Zeile
> local = *buf[0];
> Aber wieso? Ich muss doch den Zeiger doppelt dereferenzieren, einmal
> durch den *-Operator, einmal durch [].

Der []-Operator hat eine höhere Priorität, als der *-Operator. Folglich 
ist
1
uint8_t *buf[]
nicht ein pointer auf ein Array, sondern ein Array von Pointern (auf 
uint8_t). Wenn, dann muss es so heißen:
1
uint8_t (*buf)[]

Gleiches gilt für die Dereferenzierung
1
local = (*buf)[0];

von Karl H. (kbuchegg)


Lesenswert?

Michl schrieb:
> Nachtrag:
> folgender Code tut genau das was ich will

Ist aber immer noch Quatsch.

> sogar ohne Warnungen:

Das heisst nicht viel.

von Michl (Gast)


Lesenswert?

Naja ich will eigentlich kein anderes Array verwenden.
myArray enthält den Datenstrom, den ich auswerten will.

Immer häppchenweise. Beim ersten mal eben das 0x00 in meinem Beipsiel, 
danach 0x01, 0x02, usw.

von Karl H. (kbuchegg)


Lesenswert?

A. H. schrieb:
> Michl schrieb:
>> Was ich herausgefunden habe:
>> Nicht das Inkrementieren verursacht den Absturz, sondern die Zeile
>> local = *buf[0];
>> Aber wieso? Ich muss doch den Zeiger doppelt dereferenzieren, einmal
>> durch den *-Operator, einmal durch [].
>
> Der []-Operator hat eine höhere Priorität, als der *-Operator. Folglich
> ist

Hier kommt eher die Rechts-Links Regel zur Anwendung

http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html

von Peter II (Gast)


Lesenswert?

müsste doch so gehen
1
#include <stdint.h>
2
#include <stdio.h>
3
4
void doSomething(uint8_t *buf )
5
{
6
  uint8_t local;
7
8
  local = buf[0];
9
10
  printf("%x\n", local);
11
12
  buf[0]++;
13
}
14
15
int main(void)
16
{
17
    uint8_t myArray[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
18
19
    doSomething(myArray);
20
21
    return 0;
22
}

von Karl H. (kbuchegg)


Lesenswert?

Michl schrieb:
> Naja ich will eigentlich kein anderes Array verwenden.
> myArray enthält den Datenstrom, den ich auswerten will.
>
> Immer häppchenweise. Beim ersten mal eben das 0x00 in meinem Beipsiel,
> danach 0x01, 0x02, usw.


ALso willst du sowas:
1
void doSomething(uint8_t **buf )
2
{
3
  uint8_t local;
4
5
  local = *buf[0];
6
7
  printf("%x\n", local);
8
9
  (*buf)++;
10
}
11
12
int main(void)
13
{
14
    uint8_t myArray[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
15
    uint8_t* nextElem;
16
17
    nextElem = myArray;
18
19
    doSomething( &nextElem );
20
    doSomething( &nextElem );
21
22
    return 0;
23
}

willkommen in der 2-Stern Programmierung, die sich völlig analog aus der 
Situation ableitet, wie eine Funktion vorgehen muss, damit sie eine 
Variable beim Aufrufer verändern kann
1
void foo( int * a )
2
{
3
  *a = 5;
4
}
5
6
int main()
7
{
8
  int c;
9
10
  foo( &c );
11
}

der Aufrufer übergibt einen Pointer auf die Variable und die Funktion 
kann über diesen Pointer die Variable des Aufrufers ändern. Das geht für 
alle Datentypen T
1
void foo( T * a )
2
{
3
  *a = für T zulässiger Wert;
4
}
5
6
int main()
7
{
8
  T c;
9
10
  foo( &c );
11
}

In deinem Fall ist T dann eben der Datentyp 'Pointer to uint8_t', weil 
du eine Variable brauchst, in der du fest hältst, wo in deinem Array das 
nächste zu bearbeitende Byte zu finden ist.

Also ersetzte T durch uint8_t und du erhältst
1
void foo( uint8_t * * a )
2
{
3
  *a = ....
4
}
5
6
int main()
7
{
8
  uint8_t * c;
9
10
  foo( &c );
11
}

dein c ist aber nicht irgendein c, sondern enthält eine Adresse, die in 
ein Array zeigt
1
void foo( uint8_t * * a )
2
{
3
  // a ist die Adresse von c
4
  // *a ist daher der INhalt von c. Der INhalt von c, das ist aber nichts
5
  //     anderes als die Adresse des nächsten zu verarbeitenden Zeichens
6
  // daher ist **a dieses Zeichen selber
7
8
  mach was mit **a
9
10
  // und vermerken, dass dieses Zeichen somit bearbeitet wurde. Das c
11
  // vom Aufrufer soll auf den nächsten Speicherplatz im Array zeigen
12
  (*a)++;
13
}
14
15
int main()
16
{
17
  uint8_t data[] = { 5, 4, 3, 2 };
18
  uint8_t * c = data;
19
20
  foo( &c );
21
}

: Bearbeitet durch User
von Rolf Magnus (Gast)


Lesenswert?

Michl schrieb:
> sogar ohne Warnungen

Aha! Ich hatte mich schon gewundert, weil der Code davor eigentlich über 
die Typ-Unstimmigkeit hätte warnen müssen. Hat er also offenbar auch, 
aber du hast die Warnung für nicht wichtig genug befunden, um sie hier 
auch mit zu posten.

Michl schrieb:
> Der Umweg über
> help() ist ja letzendlich gleichwertig mit der von Rolf Magnus
> vorgeschlagenen Lösung, den lokalen Pointer anzulegen.

Ja, richtig. In dem Fall ist halt der Parameter buf der lokale Pointer.

Karl Heinz schrieb:
> Das hier
> void doSomething(uint8_t *buf[])
> besagt, dass buf ein Array von Pointer ist, wobei jeder Pointer auf
> (mindestens) einen uint8_t zeigt.

Nein. buf ist ein Zeiger auf einen Zeiger.

Karl Heinz schrieb:
> Aber wo sind diese Daten in deinem Programm.
> Wenn du sagst, du willst ein anderes Array benutzen (und das tust du mit
> deinem Code), dann muss es auch ein anderes Array geben. Wo ist dieses?

Er will nicht ein anderes Array benutzen, sondern mittels eines Pointers 
durch ein Array iterieren.

A. H. schrieb:
> Folglich ist
> uint8_t *buf[]
> nicht ein pointer auf ein Array, sondern ein Array von Pointern (auf
> uint8_t).

buf ist ein Funktionsparameter. Da haben die Klammern nichts mit Arrays 
zu tun. buf ist schlicht und ergreifend ein Zeiger auf einen Zeiger! 
uint8_t *buf[] ist als Parameter zu 100% äquivalent zu uint8_t ** buf.

von tictactoe (Gast)


Lesenswert?

Karl Heinz schrieb:
> local = *buf[0];

Genau genommen
1
local = (*buf)[0];
was aber wegen der Null in den eckigen Klammern (und nur deswegen!) auf 
das gleiche raus läuft wie das, was du geschrieben hast.

von Bitflüsterer (Gast)


Lesenswert?

Wozu man vielleicht noch ergänzend folgendes erwähnen kann.

Der Name eines Vektors ist nicht gleichbedeutend mit einer Variablen, 
die einen Zeiger enthält. Demzufolge kann man unter diesem Namen auch 
keinen Zeiger manipulieren.

Vielmehr ist der Name eines Vektors synonym zu einem Zeiger auf diesen 
Vektor. D.h. bei Verwendung des Vektornamens im Code wird an seiner 
Stelle, während der Compilation resp. Linken, ein Zeiger auf das erste 
Element verwendet. Hingegen existiert der Zeiger zur Laufzeit in keiner 
Weise als "Ding", das im Quellcode irgendwie nennbar wäre.

von Karl H. (kbuchegg)


Lesenswert?

Rolf Magnus schrieb:

>> void doSomething(uint8_t *buf[])
>> besagt, dass buf ein Array von Pointer ist, wobei jeder Pointer auf
>> (mindestens) einen uint8_t zeigt.
>
> Nein. buf ist ein Zeiger auf einen Zeiger.

Wenn wir uns nur auf den Datentyp konzentrieren, dann schon.
Allerdings ist das hier in einer Situation, in der Arrays automatisch zu 
Zeigern decayen. Somit haben wir beide recht.

Letzten Endes geht es darum, dass in C die alternative Schreibweisen
1
void foo( T * arg )
und
1
void foo( T arg[] )
technisch gesehen aufs gleiche rauslaufen, auch wenn sie etwas anders 
suggerieren.

: Bearbeitet durch User
von Bitflüsterer (Gast)


Lesenswert?

> willkommen in der 2-Stern Programmierung, ...

Grins.

von A. H. (ah8)


Lesenswert?

Karl Heinz schrieb:
> A. H. schrieb:
>> Michl schrieb:
>>> Was ich herausgefunden habe:
>>> Nicht das Inkrementieren verursacht den Absturz, sondern die Zeile
>>> local = *buf[0];
>>> Aber wieso? Ich muss doch den Zeiger doppelt dereferenzieren, einmal
>>> durch den *-Operator, einmal durch [].
>>
>> Der []-Operator hat eine höhere Priorität, als der *-Operator. Folglich
>> ist
>
> Hier kommt eher die Rechts-Links Regel zur Anwendung
>
> http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html

Naja, diese Regel würde ich eher als eine Gedankenstütze für 
Programmierer verstehen, denn der Compiler richtet sich bei der Analyse 
des Codes und dem Aufbau des Syntaxbaumes definitiv nach den 
Präzedenzregeln: Hat er den Identifikator erkannt (in diesem Fall buf) 
muss er entscheiden ob er den indizieren oder dereferenzieren soll. Da 
die Indizierung die höhere Präzedenz hat, wird er zunächst annehmen, 
dass es sich um ein Array handelt, dessen Element dann dereferenziert 
werden soll, also ein Pointer sein muss.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

tictactoe schrieb:
> Karl Heinz schrieb:
>> local = *buf[0];
>
> Genau genommen
>
1
> local = (*buf)[0];
2
>
> was aber wegen der Null in den eckigen Klammern (und nur deswegen!) auf
> das gleiche raus läuft wie das, was du geschrieben hast.

Danke.
Den hab ich beim kopieren übersehen.

von Michl (Gast)


Lesenswert?

Eins vorweg KH: dein Code funktioniert perfekt:
1
void doSomething(uint8_t **buf )
2
{
3
  uint8_t local;
4
5
  local = *buf[0];
6
7
  printf("%x\n", local);
8
9
  (*buf)++;
10
}
11
12
int main(void)
13
{
14
    uint8_t myArray[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
15
    uint8_t* nextElem;
16
17
    nextElem = myArray;
18
19
    doSomething( &nextElem );
20
    doSomething( &nextElem );
21
22
    return 0;
23
}

Aber: ändere ich die Zeile
1
local = *buf[0];
in doSomething() zu
1
local = *buf[1];
schmiert mein Programm wieder ab.

Langsam glaube ich dass hier mein eigentliches Problem liegt.

Worran kann das liegen?

von Rolf Magnus (Gast)


Lesenswert?

tictactoe schrieb:
> Genau genommen
> local = (*buf)[0];

Da hier ja eh keine wirkliche Array-Indizierung benötigt wird, sondern 
einfach nur einen Zeiger auf einen Zeiger zweimal dereferenziert werden 
soll, würde ich gleich schreiben:
1
local = **buf;

Karl Heinz schrieb:
> Wenn wir uns nur auf den Datentyp konzentrieren, dann schon.
> Allerdings ist das hier in einer Situation, in der Arrays automatisch zu
> Zeigern decayen. Somit haben wir beide recht.

Naja, du meintest vorhin:

Karl Heinz schrieb:
> ist etwas völlig anderes und auch wenn du mittels eines Adressoperators
> erst mal alles ruhig gestellt hast, ändert das nichts an der Tatsache,
> dass deine Daten nicht den Aufbau haben, den die Funktion voraussetzt.

Die Daten haben aber durchaus den Aufbau, den die Funktion voraussetzt, 
auch wenn das auf den ersten Blick nicht so aussieht.

> Du kannst eine Kuh weiß anstreichen, aber das macht sie nicht zum
> Schimmel.

Tatsächlich ist es hier so, daß es bereits ein Schimmel ist, der nur 
durch die ulkige Syntax von C wie eine Kuh aussieht. ;-)

Karl Heinz schrieb:
> technisch gesehen aufs gleiche rauslaufen, auch wenn sie etwas anders
> suggerieren.

Ebenth!

von Bitflüsterer (Gast)


Lesenswert?

>Worran kann das liegen?

Beitrag "Re: Zeiger auf Array?"

von Michl (Gast)


Lesenswert?

Bitflüsterer schrieb:
> Beitrag "Re: Zeiger auf Array?"

Danke, im Eifer des Gefechts übersehen!

von Karl H. (kbuchegg)


Lesenswert?

Rolf Magnus schrieb:

> Die Daten haben aber durchaus den Aufbau, den die Funktion voraussetzt,
> auch wenn das auf den ersten Blick nicht so aussieht.

In seinem Originalbeispiel nicht.
Da fehlte die Zwischenebene einer Pointer-Variablen

@TO
>
> Aber: ändere ich die Zeile
> local = *buf[0];
> in doSomething() zu
>
> local = *buf[1];

Mittels
1
    uint8_t myArray[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
2
    uint8_t* nextElem;
3
4
    nextElem = myArray;

hast du jetzt das hier aufgebaut
1
   nextElem
2
   +-------+                   +------+
3
   |  o----------------------->| 0x00 |
4
   +-------+                   +------+
5
                               | 0x01 |
6
                               +------+
7
                               | 0x02 |
8
                                .....

Durch die Übergabe an die Funktion hast du eine weitere Ebene eingebaut
1
   buff
2
  +-------+
3
  |  o-----------+
4
  +-------+      |
5
                 |
6
                 v
7
              +--------+             +------+
8
     nextElem |  o------------------>| 0x00 |
9
              +--------+             +------+
10
                                     | 0x01 |
11
                                     +------+
12
                                     | 0x02 |
13
                                       ....

was besagt nun
1
    *buf[0]

es besagt: verfolge den Zeiger buf und dort wo er hin führt, nimm das 
Array Element 0 (das ist jetzt etwas salopp formuliert, aber darauf 
läuft es hinaus).
Nun, wo zeigt denn buf hin?
buf zeigt auf nextElemen (auch wenn buf davon nichts weiß). nextElem ist 
kein Array, aber durch den Array-Pointer Dualismus ist das bei [0] ok. 
Da passiert nichts.

Ganz anders aber bei
1
    *buf[1]

jetzt willst du durch das [1] hier
1
   buff
2
  +-------+
3
  |  o-----------+
4
  +-------+      |
5
                 |
6
                 v
7
              +--------+             +------+
8
     nextElem |  o------------------>| 0x00 |
9
              +--------+             +------+
10
                ######               | 0x01 |
11
                                     +------+
12
                                     | 0x02 |
13
                                       ....
zugreifen und dir von hier den Pointer holen, mit dem du dann auf die 
eigentlichen Daten weiter zugreifst (was dann der * in *buf[1] gemacht 
hätte).
Nur sieht man in der Grafik auch: An der Stelle der ####### ist gar kein 
Zeiger! Du greifst auf irgendeinen Speicher zu, und was immer auch dort 
im Speicher steht, es wird als die Adresse aufgefasst, mit der dann 
weiter dereferenziert wird.

Und das geht dann in die Hose.

Und ich schliesse mich Rolf an.
Da das, worauf buf zeigt, in erster Linie gar kein Array ist, würde ich 
das auch nicht mit Array Syntax schreiben, sondern als
1
   **buf
schreiben. Die Array Indizierung, die zwar bei [0] das identische 
Verhalten hervorruft, führt einen hier in die Irre: sie gaukelt etwas 
vor, was nicht da ist: Das worauf buf zeigt ist kein Array (auch wenn es 
eines sein könnte und sich an der Syntax dadurch nichts ändern würde)

: Bearbeitet durch User
von A. H. (ah8)


Lesenswert?

Rolf Magnus schrieb:
> A. H. schrieb:
>> Folglich ist
>> uint8_t *buf[]
>> nicht ein pointer auf ein Array, sondern ein Array von Pointern (auf
>> uint8_t).
>
> buf ist ein Funktionsparameter. Da haben die Klammern nichts mit Arrays
> zu tun. buf ist schlicht und ergreifend ein Zeiger auf einen Zeiger!
> uint8_t *buf[] ist als Parameter zu 100% äquivalent zu uint8_t ** buf.

Einigen wir uns auf die Formulierung: Der Compiler wandelt implizit eine 
Array von Pointern in ein Pointer auf Pointer? Syntaktisch bleibt der 
Ausdruck nämlich ein  Array von Pointern, ungeachtet der semantischen 
Kapriolen, die der Compiler dann damit treibt (und die sicherlich zum 
weniger glücklichen historischen Erbe von C gehören). Technisch gesehen 
ist Deine Aussage natürlich richtig, was den Lerneffekt betrifft hielte 
ich in diesem Fall die Unterscheidung zwischen Pointer auf Array und 
Array von Pointern aber für wichtiger, die spätestens dann relevant 
wird, wenn die Indexklammern nicht mehr leer (bzw. != 0) sind und die 
offenbar nicht ganz einfach ist, wie der immer noch fehlerhafte 
Quelltext und die wiederholten Fragen des TO deutlich zeigen. Man möge 
mir die kleine technische Ungenauigkeit an dieser Stelle daher verzeihen 
:-)

: Bearbeitet durch User
von Rolf Magnus (Gast)


Lesenswert?

A. H. schrieb:
> Syntaktisch bleibt der Ausdruck nämlich ein  Array von Pointern,
> ungeachtet der semantischen Kapriolen, die der Compiler dann damit treibt
> (und die sicherlich zum weniger glücklichen historischen Erbe von C
> gehören).

Es ist eines der Dinge in C, die ursprünglich die Sprache intuitiver 
hätten machen sollen, aber leider (zumindest für jemanden, der die 
Sprache wirklich verstehen will) genau das Gegenteil tun. Gut - kann man 
heute nicht mehr ändern.
Um es nochmal auf die schöne Analogie von Karl-Heinz zu beziehen: Ich 
will eine Kuh und bekomme stattdessen einen Schimmel, der so angemalt 
ist, daß er aussieht wie eine Kuh. Spätestens beim melken fällt einem 
dann auf, daß da was nicht stimmt.

A. H. schrieb:
> Man möge mir die kleine technische Ungenauigkeit an dieser Stelle daher
> verzeihen :-)

Das ist vielleicht ein anderer pädagogischer Ansatz. Ich erkläre lieber 
einem Einsteiger die Dinge korrekt, auch wenn's das am Anfang 
komplizierter macht. Es mag aber auch Vorteile haben, manchmal nicht so 
genau auf die Details zu achten, um den Einstieg zu erleichtern.

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.