Forum: Mikrocontroller und Digitale Elektronik Funktionsaufruf in C mit array type pointer


von Max (Gast)


Lesenswert?

Hallo zusammen,

ich versuche inerhalb einer Funktion den Inhalt eines Arrays mit fester 
Länge das per typedef definiert wurde zu ändern. Hier das vereinfachte 
Problem:
1
#include <stdio.h>
2
#include <stdint.h>
3
4
typedef uint16_t AT_eightWords[8];
5
6
void Update(AT_eightWords * eW)
7
{
8
    eW[0] = 42;
9
    eW[1] = 43;
10
    eW[2] = 44;
11
    eW[3] = 45;
12
}
13
14
AT_eightWords GLOBAL_eW;
15
16
int main()
17
{
18
    printf("Hello World\n");
19
    
20
    Update(&GLOBAL_eW);
21
    
22
    printf("%d %d %d %d", GLOBAL_eW[0], GLOBAL_eW[1], GLOBAL_eW[2], GLOBAL_eW[3]);
23
24
    return 0;
25
}

Der Compiler meint zu "eW[0] = 42;":
1
error: assignment to expression with array type

Mit
1
    *eW[0] = 42;
2
    *eW[1] = 43;
3
    *eW[2] = 44;
4
    *eW[3] = 45;
meckert der Compiler nicht aber es ergibt sich die Ausgabe "42 0 0 0"

Wie geht es richtig?

von Walter T. (nicolas)


Lesenswert?

Wenn Du auf die Zeigerschreibweise bestehst, wird es wohl auf ein
1
 *(eW+0) = 42;
hinauslaufen.

(Ehrlich gesagt wundert mich, dass Du bei Deiner Variante keinen 
Segmentation Fault einsammelst.)

: Bearbeitet durch User
von Max (Gast)


Lesenswert?

Danke für die schnelle Antwort.
Zu:
1
    *(eW+0) = 42;
2
    *(eW+1) = 43;
3
    *(eW+2) = 44;
4
    *(eW+3) = 45;
meint der Compiler auch: "error: assignment to expression with array 
type"
(getestet mit https://www.onlinegdb.com/online_c_compiler)

von Oliver S. (oliverso)


Lesenswert?

Stichwort operator precedence. [] sticht *

Daher:

(*eW)[0] = 42;

Oliver

von Walter T. (nicolas)


Lesenswert?

Hoppla. Ja. Mein Fehler. Ich habe das ungewöhnliche Typedef übersehen. 
Hat das einen bestimmten Grund?

Beitrag #6759195 wurde vom Autor gelöscht.
von Klaus W. (mfgkw)


Lesenswert?

...
void Update(AT_eightWords eW)
...
Update(GLOBAL_eW);
...

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Max schrieb:
> typedef uint16_t AT_eightWords[8];
> void Update(AT_eightWords * eW)

Das ist quasi ein Pointer auf ein Array, also ptr auf ptr.

Vermutlich willst Du nur ein Array, dann lass das * vor eW weg.

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Walter T. schrieb:
> Ich habe das ungewöhnliche Typedef übersehen.
Der dürfte auch das problem sein. Das wäre dann ja quasi sowas hier und 
das passt nicht wirklich:
1
void Update(uint16_t[8] * eW)

So dürfte das eher hinkommen:
1
uint16_t GLOBAL_eW[8];
2
void Update(uint16_t *eW)
3
{
4
  eW[0] = 42;
5
  eW[1] = 43;
6
  eW[2] = 44;
7
  eW[3] = 45;
8
}
9
int main()
10
{
11
  Update(GLOBAL_eW);
12
...

oder wenn schon unbedingt typedef:
1
typedef uint16_t AT_eightWords;
2
AT_eightWords GLOBAL_eW[8];
3
void Update(AT_eightWords *eW)
4
{
5
  eW[0] = 42;
6
  eW[1] = 43;
7
  eW[2] = 44;
8
  eW[3] = 45;
9
}
10
int main()
11
{
12
  Update(GLOBAL_eW);
13
...

von Rolf M. (rmagnus)


Lesenswert?

A. S. schrieb:
> Max schrieb:
>> typedef uint16_t AT_eightWords[8];
>> void Update(AT_eightWords * eW)
>
> Das ist quasi ein Pointer auf ein Array, also ptr auf ptr.

Ein Pointer auf ein Array ja, aber kein Pointer auf einen Pointer.

> Vermutlich willst Du nur ein Array, dann lass das * vor eW weg.

Generell würde ich typedefs für Arrays vermeiden, weil das nur 
Verwirrung stiftet.

Irgend W. schrieb:
> Das wäre dann ja quasi sowas hier und
> das passt nicht wirklich:
> void Update(uint16_t[8] * eW)

Genauer:
1
void Update(uint16_t (*eW)[8])

… ein Zeiger auf ein Array aus 8 chars.

von foobar (Gast)


Lesenswert?

rmagnus schrieb:
> Generell würde ich typedefs für Arrays vermeiden, weil das nur
> Verwirrung stiftet.

In der Tat.  Eine Variable solchen Typs zu übergeben sieht aus wie ein 
"call by value" ist aber tatsächlich ein "call by reference".  D.h., 
ohne die Definition des Types zu kennen (die Definition der Variable 
reicht nicht!), kann man nicht erkennen, was der Code macht.

von Dirk B. (dirkb2)


Lesenswert?

Rolf M. schrieb:
> Ein Pointer auf ein Array ja, aber kein Pointer auf einen Pointer.

In der Funktion dann aber schon.

von mh (Gast)


Lesenswert?

Dirk B. schrieb:
> Rolf M. schrieb:
>> Ein Pointer auf ein Array ja, aber kein Pointer auf einen Pointer.
>
> In der Funktion dann aber schon.

Nein.

von Oliver S. (oliverso)


Lesenswert?

Dirk B. schrieb:
> Rolf M. schrieb:
>> Ein Pointer auf ein Array ja, aber kein Pointer auf einen Pointer.
>
> In der Funktion dann aber schon.
1
void Update(AT_eightWords * eW)
2
{
3
  printf("%llu\n", sizeof(*eW));
4
}

Erst sagen, was da raus kommt, dann ausprobieren.

Oliver

von Florian K. (florian_k89)


Lesenswert?

Ändere die Update(AT_eightWords *eW) zu
Zu Update(uint8_t *eW)
Dein Datentyp ist ein 8 Felder großes Array
davon machst du einen Pointer *eW und willst ihn so behandeln als wäre 
er ein einfacher Pointer. Ich denke das geht schief. In der Update () 
brauchst du eigentlich nur einen einfachen uint8_t Pointer.

von Philipp K. (philipp_k59)


Lesenswert?

Max schrieb:
> *(eW+0) = 42;

Müsste das bei *eW+0 nicht mit void Update(AT_eightWords **eW) 
kombiniert werden?

Dann muss man aber die Klammern rausnehmen.

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Oliver S. schrieb:
> Erst sagen, was da raus kommt, dann ausprobieren.

Die Größe des Arrays. Es ist aber egal, was da herauskommt.

Ein Array und ein Pointer sind bei einer Übergabe äquivalent.

Wenn der TO sich dessen klar wird, kann er auch herausklamüsern, was da 
passiert. Und ja, je nach Konstrukt was anderes. Beim TO

Max schrieb:
> *eW[0] = 42;
> *eW[1] = 43;
> *eW[2] = 44;
> *eW[3] = 45;

ist halt nur der erste Zugriff OK, der zweite zeigt auf Element 0 im 
Array dahinter (also nicht nächstes Element, sondern 8 Elemente, also 
außerhalb es eigentlichen Arrays.

Wenn er nicht nur "quasi" wie ich schrieb, sondern echt einen Ptr auf 
Ptr übergeben hätte, dann wäre auch der erste Zugriff OK, aber danach 
würde er die nächsten X Bytes als neuen Ptr interpretieren.

Egal. Den Mechnismus des TO habe ich erklärt, die Unterschiede zwischen 
Ptr und Array bei der Übergabe mögen Korinthen** unter sich ausmachen.

von Rolf M. (rmagnus)


Lesenswert?

A. S. schrieb:
> Oliver S. schrieb:
>> Erst sagen, was da raus kommt, dann ausprobieren.
>
> Die Größe des Arrays. Es ist aber egal, was da herauskommt.

Das ist nicht egal. Wäre es ein Pointer auf einen Pointer, dann käme da 
eben nicht die Größe des Arrays raus.

> Ein Array und ein Pointer sind bei einer Übergabe äquivalent.

Naja, es kommt in der Funktion ein Pointer auf das erste Element an. Das 
Array "zefällt" zu einem Pointer. Aber das gilt nicht rekursiv. Ein 
Pointer auf ein Array bleibt auch nach Übergabe ein solcher und zerfällt 
nicht zu einem Pointer auf einen Pointer.

> Egal. Den Mechnismus des TO habe ich erklärt, die Unterschiede zwischen
> Ptr und Array bei der Übergabe mögen Korinthen** unter sich ausmachen.

Der ist hier allerdings für das Verständnis essenziell.

von A. S. (Gast)


Lesenswert?

Rolf M. schrieb:
> Der ist hier allerdings für das Verständnis essenziell.

Um zu verstehen, was genau der Fehler macht. In beiden Fällen ist der 
Fehler fatal und die Wirkung gleich: Ein unbeteiligter Speicherbereich 
wird verwendet und führt zu undefiniertem Verhalten.

Ich habe natürlich auch überlegt, ob ich dabei schreiben sollte, wo er 
jetzt hingreift. Es hilft dem TO aber nicht beim Verständnis. Und die 
Diskussion, wann ein Array zu einem einzelnen Ptr zerfällt, ist hier 
sicher nicht dem TO angemessen. Anscheinend ja nichtmal den meisten 
Diskutanten.

von mh (Gast)


Lesenswert?

A. S. schrieb:
> Und die
> Diskussion, wann ein Array zu einem einzelnen Ptr zerfällt, ist hier
> sicher nicht dem TO angemessen. Anscheinend ja nichtmal den meisten
> Diskutanten.

Dieses schrecklich komplizierte Konzept, das im Standard 3 ganze Zeilen 
belegt.

von A. S. (Gast)


Lesenswert?

mh schrieb:
> Dieses schrecklich komplizierte Konzept, das im Standard 3 ganze Zeilen
> belegt.

Egal. Schau Dir den Thread hier an, wer da alles Probleme hat.

Oder schau Dir an, wie oft Du im Code sowas "int a[100]; ... foo(&a);" 
findest, also dass der Adressoperator auf ein Array gesetzt wird.

Wenn Du eine gute Erklärung hast, verlinke sie gerne. Aber hier ist es 
für den TO und andere mit ähnlicher Lernkurve schwer, noch was 
rauszuziehen.

von Al Fine (Gast)


Lesenswert?

A. S. schrieb:
> Wenn Du eine gute Erklärung hast, verlinke sie gerne. Aber hier ist es
> für den TO und andere mit ähnlicher Lernkurve schwer, noch was
> rauszuziehen.

Harte Realiät: Der TO ist idR nach einiger Zeit meist nur noch ein 
Beispiel.

Die Info könnte doch für irgendwen nützlich sein.

von mh (Gast)


Lesenswert?

A. S. schrieb:
> mh schrieb:
>> Dieses schrecklich komplizierte Konzept, das im Standard 3 ganze Zeilen
>> belegt.
>
> Egal. Schau Dir den Thread hier an, wer da alles Probleme hat.
>
> Oder schau Dir an, wie oft Du im Code sowas "int a[100]; ... foo(&a);"
> findest, also dass der Adressoperator auf ein Array gesetzt wird.

Bist du sicher, dass du damit keine Probleme hast? Oder was ist deiner 
Meinung nach das Problem in deinem Beispiel?

> Wenn Du eine gute Erklärung hast, verlinke sie gerne. Aber hier ist es
> für den TO und andere mit ähnlicher Lernkurve schwer, noch was
> rauszuziehen.
Der Standard, oder wenn man dazu nicht in der Lage ist, reicht 
https://en.cppreference.com/w/c/language/conversion. Das was auf der 
Seite steht, sollte man als C Entwickler kennen.

von A. S. (Gast)


Lesenswert?

mh schrieb:
> Der Standard, oder wenn man dazu nicht in der Lage ist, reicht
> https://en.cppreference.com/w/c/language/conversion. Das was auf der
> Seite steht, sollte man als C Entwickler kennen.

Der Link ist jetzt Dein konstruktiver Beitrag hier im Thread? Und da 
soll der TO oder jemand anders erkennen, was bei

A. S. schrieb:
> int a[100]; ... foo(&a);"

falsch ist? Jedenfalls erwarte ich da foo(a) oder foo(&a[0]) oder von 
mir aus auch foo(a+0). Oder irgendwas anderes, was foo erwartet. Nur 
"&a" gibt (wenn foo eine Funktion ist) in keinem Fall Sinn.

Ja, Compiler akzeptieren das, doch trägt es zu der Verwirrung bei, der 
der TO in seinem Code erlegen war.

von mh (Gast)


Lesenswert?

A. S. schrieb:
> Jedenfalls erwarte ich da foo(a) oder foo(&a[0]) oder von
> mir aus auch foo(a+0). Oder irgendwas anderes, was foo erwartet. Nur
> "&a" gibt (wenn foo eine Funktion ist) in keinem Fall Sinn.

foo(a), foo(&a[0]) und foo(a+0) sind offensichtlich falsch! Mein foo ist 
übrigens void foo(int (*a)[100]).

von Rolf M. (rmagnus)


Lesenswert?

A. S. schrieb:
> a, Compiler akzeptieren das,

Wenn die Typen nicht passen, meldet das der Compiler eigentlich.

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.