Forum: Compiler & IDEs Zeiger auf Struktur


von Rudi (Gast)


Lesenswert?

Hallo,

ich schlag mich grad mal wieder mit einem Zeiger Problem herum.

Ich habe einen globalen Zeiger auf eine Struktur. Diese Struktur hat 
unter anderem als Member einen Zeiger auf Daten:
1
typedef struct{
2
  //member;
3
  //nochnmember;
4
  uint8 *data;
5
}my_struct_t;
6
7
my_struct_t *my_struct_ptr;

Nun würde ich gerne in einer Funktion auf den Datenteil dieser Struktur 
zugreifen.

Folgendes funktioniert:
1
void foo(void)
2
{
3
  uint8 *local_ptr;
4
  local_ptr = my_struct_ptr->data;
5
6
  local_ptr[0] = ...;
7
  local_ptr[x] = ...;
8
}

Probier ichs ohne Umweg über den lokalen Zeiger
1
  my_struct_ptr->data[0] = ...;

Meckert mirs der Compiler an.
"expression must be a pointer to a complete object type"

Damit kann ich nichts anfangen. Bin grad bisl ratlos, kann mir jemand 
weiterhelfen?

Viele Grüße,
Rudi

von Karl H. (kbuchegg)


Lesenswert?

Rudi schrieb:

>
1
>   my_struct_ptr->data[0] = ...;
2
>
>
> Meckert mirs der Compiler an.
> "expression must be a pointer to a complete object type"

der SChlüssel ist an dieser Stelle die Phrase "complete object type". An 
der Stelle an der du das machst, weiß der Compiler nicht, wie ein 
my_struct_t aussieht.
Wahrscheinlich hast du nur vergessen, das Header File zu inkludieren, in 
dem die struct definiert ist.

von Rudi (Gast)


Lesenswert?

Doch, die kennt er.

Allerdings hab ich mich getäuscht was den .data member betrifft.

Dieser ist vom Typ void* anstatt uint8*.

Aber das sollte doch keine Rolle spielen?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Rudi schrieb:
> Dieser ist vom Typ void* anstatt uint8*.
>
> Aber das sollte doch keine Rolle spielen?

O doch, void-Pointer können nicht dereferenziert werden.

von Rudi (Gast)


Lesenswert?

Hm garned bedacht. Macht aber Sinn, immerhin kennt er ja dann die Größe 
des Elements nicht, sodass er spätestens beim Zugriff mitells [0], 
[1]... überfordert wäre, richtig?

von Karl H. (kbuchegg)


Lesenswert?

Rudi schrieb:
> Hm garned bedacht. Macht aber Sinn, immerhin kennt er ja dann die Größe
> des Elements nicht, sodass er spätestens beim Zugriff mitells [0],
> [1]... überfordert wäre, richtig?


richtig.
In die Indexberechnung geht ja logischerweise auch immer die sizeof des 
Typs ein um zu Berechnen, wieviele Bytes zwischen Anfang des Arrays und 
dem interessierenden Arrayelement liegen.

von Rudi (Gast)


Lesenswert?

Kann man das dann irgendwie umcasten?

von Karl H. (kbuchegg)


Lesenswert?

Rudi schrieb:
> Kann man das dann irgendwie umcasten?

natürlich kann man umcasten.
Allerdings solltest du dich fragen, ob da prinzipiell der void* 
überhaupt eine gute Idee war. Wenn du sowieso den Datentyp kennst auf 
den du casten willst, warum steht dann der nicht gleich da drinnen?

Es gibt natürlich Fälle, an denen man bewusst einen void* einsetzt. Zb 
wenn ein Modul eine Strukturobjekt rausgeben muss und nicht haben will, 
dass sich derjenige der das Objekt benutzt, an den Daten vergreift. Der 
void* ist dann mehr oder weniger ein 'Finger weg! An diesen Daten hast 
du nichts zu suchen'.

von Rudi (Gast)


Lesenswert?

Weil der Typ von einer Lib, genauer gesagt dem LwIP Stack, vorgegeben 
ist.

Dort gibt es die Struktur pbuf, die als Buffer für ein- und ausgehende 
Datenströme dient. Und die zeigt leider auf void.

Wie müsste denn der Cast aussehen?

von Karl H. (kbuchegg)


Lesenswert?

Rudi schrieb:
> Weil der Typ von einer Lib, genauer gesagt dem LwIP Stack, vorgegeben
> ist.
>
> Dort gibt es die Struktur pbuf, die als Buffer für ein- und ausgehende
> Datenströme dient. Und die zeigt leider auf void.

Eigenartig.
Sicher, dass das so gedacht ist, dass du auf diese Werte zugreifen 
darfst?

(Vielleicht wollten sie aber auch den Aufrufer auch nur dazu zwingen, 
dass er ein wenig darüber nachdenkt, was das eigentlich für Daten sind, 
die er da bekommt: int, float, double, bytes, ...
üblich ist es allerdings nicht. Solche Buffer werden normalerweise als 
Bytes angesehen - also 'unsigned char' Arrays.


>
> Wie müsste denn der Cast aussehen?

Na ja, eben den Pointer entsprechend umcasten

    ((uint8_t*)(my_struct_ptr->data))[0]

sieht ein wenig seltsam aus, daher wäre wohl der Weg über eine 
Zwischenvariable der syntaktisch lesbarere. Der Compiler schmeisst die 
Variable ja sowieso wieder raus.

von Rudi (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Eigenartig.
> Sicher, dass das so gedacht ist, dass du auf diese Werte zugreifen
> darfst?

Laut der (leider sehr dürftigen) Doku im Internet und den Beispielen die 
ich gefunden habe wird das durchaus so gemacht.

Die allokierte Buffergröße wird auch in der Struktur abgelegt, somit ist 
es wohl dem Benutzer überlassen den Zugriff ordentlich zu regeln.

Aber ich muss sagen, der LwIP Stack ist nicht gerade ein Musterbeispiel 
für lesbaren Code...

von Karl H. (kbuchegg)


Lesenswert?

Rudi schrieb:
> Karl Heinz Buchegger schrieb:
>> Eigenartig.
>> Sicher, dass das so gedacht ist, dass du auf diese Werte zugreifen
>> darfst?
>
> Laut der (leider sehr dürftigen) Doku im Internet und den Beispielen die
> ich gefunden habe wird das durchaus so gemacht.
>
> Die allokierte Buffergröße wird auch in der Struktur abgelegt, somit ist
> es wohl dem Benutzer überlassen den Zugriff ordentlich zu regeln.

:-)
Womit sich dann allerdings die Frage erhebt, wie denn die Buffergröße 
angegeben ist. Sind das Bytes, Floats, andere Objektgrößen, Äpfel, 
Birnen ...
:-)

(Ist jetzt keine Kritik an dir. Soll dir nur zeigen, dass an dieser 
Stelle der void* nicht sehr logisch ist. WEnn ich schon die Buffergröße 
(in Bytes) in der Struktur habe, dann wäre es logisch auch den Pointer 
als Byte-Pointer anzulegen. Dann passen beide in den Einheiten zusammen.

Ändert nichts daran, dass man des öfteren sowieso umcasten muss. Aber 
dann muss man ja auch die Längenangabe umrechnen und kann das so gesehen 
ohnehin nicht verpassen.

von Rudi (Gast)


Lesenswert?

Das Ganze ist wohl einfach "historisch gewachsen" und generell sehr 
undurchsichtig.

Die Puffer sind außerdem einfach verkettet und speichern noch ihr 
nächstes Element, die Gesamtgröße der Kette und die Größe des jeweiligen 
Segments. Alle Angaben in Bytes.
Und halt das Datenelement als void*.

Aber das geht ja noch, richtig dreckig sind erst die Präprozessor 
Spielereien...

von Rolf Magnus (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> (Ist jetzt keine Kritik an dir. Soll dir nur zeigen, dass an dieser
> Stelle der void* nicht sehr logisch ist.

Warum nicht? Ist auch nicht anders, als auf dem PC. Wenn ich da mit 
read() aus einem Socket lese, übergebe ich auch einen void* und einen 
size_t, was er dann nutzt, um seine Daten reinzuschreiben.
Es geht um einen Puffer mit (für das API) völlig unbekanntem Inhalt. Da 
erscheint mir ein void* eigentlich recht passend. malloc() gibt ja auch 
keinen Zeiger auf char zurück, nur weil ich ihm als Parameter die 
gewünschte Größe in Bytes angebe.

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.