Forum: Compiler & IDEs Zeiger Funktion


von Gast G. (max92)


Lesenswert?

Hallo zusammen,

ich hätte eine frage zu dieser Zeiger Funktion

uint16_t *p = (uint16_t*)&structur;.

Ich verstehe das ein Zeiger der Größe 16 Bit von typ integer auf eine 
variable bzw. structur zeigt und dann in einem Zeiger vom Typ integer 
der Größe 16 Bit gespeichert wird, ist das so richtig?

Denn ich verstehe die Funktion nicht ganz bzw. den sinn, ich konnte auch 
hier eine Erklärung finden die ich leider nicht ganz verstanden habe.
Könnt ihr mir dabei helfen?

Danke

: Verschoben durch User
von René H. (Gast)


Lesenswert?

Die Adresse von "structur" wird in einem uint16_t Zeiger abgelegt.

Grüsse,
René

von hicki-hacki (Gast)


Lesenswert?

Hallo,
du sagst deinem Pointer vom Datentyp uint16_t, dass er jetzt auf die 
Variable structur zeigt, die eigentlich nicht vom Datentyp uint16_t ist.

von Nop (Gast)


Lesenswert?

Gast G. schrieb:
> uint16_t *p = (uint16_t*)&structur;.

Oh-oh. Das riecht schwer nach pointer aliasing und undefiniertem 
Verhalten.

> Ich verstehe das ein Zeiger der Größe 16 Bit

Nein. Die Größe des Zeigers selber hat nichts mit dem Datentyp zu tun, 
auf den er zeigt. Der Zeiger selber ist so groß, wie die Adressen auf 
Deinem Zielsystem sind.

Also unter Linux-32 wäre der Zeiger selber 32 bit, auch wenn er auf 
einen uint16_t zeigt. Unter Linux-64 hingegen hätte dieser Zeiger 64 
bit.

> von typ integer

Nein. Der Zeiger hat nicht den Typ uint16_t, sondern den Typ uint16_t*.

Also was passiert, man nimmt sich die Speicheradresse der Struktur (mit 
dem &). Das ist erstmal ein Pointer vom Typ t_structur* (wenn t_structur 
der Typ von structur ist). Dieser wird dann mit dem Pointercast 
uminperpretiert, daß dieselbe Adresse jetzt als uint16_t* interpretiert 
wird. Dann wird dieser Pointer in einem Pointer vom passenden Typ 
uint16_t* gespeichert.

Und wenn Du jetzt noch sowohl mit dem Pointer p (per Dereferenzierung) 
als auch mit der eigentlichen Strukturvariablen structur auf diese 
Struktur zugreifst, dann hast Du undefiniertes Verhalten wegen pointer 
aliasing. Weil Du dann mit verschiedenen Pointertypen auf derselben 
Speicheradresse herummachst.

Das gibt sehr lustige Fehler abhängig von Optimierungslevel und 
Tageslaune des Compilers.

von Wilhelm M. (wimalopaan)


Lesenswert?

Also wenn vorher
uint16_t structur = 0;

stünde, dann zeigt p danach einfach auf structur:

uint16_t *p = (uint16_t*)&structur;

Also, nirgendwo UB!!!

Auf bei
struct A {
 uint8_t a;
 uint8_t b;
} structur = {};

ist alles ok: man beschreibt beide Strukturkomponenten a und b. Auch 
kein UB.

Nur
uint8_t structur = 0;

o.ä. für zu UB.

Macht C nicht schlechter als es ist ;-)

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Wilhelm M. schrieb:
> Also, nirgendwo UB!!!

Da noch nicht. Aber die Chance, das das, was dann später mit dem Pointer 
angestellt wird, zweifelhaft ist, ist sehr groß. Es muß ja einen Grund 
für diesen Hack geben.

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Ja und warum schreibst Du das dann?

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Also wenn vorher
> uint16_t structur = 0;

Das geht natürlich, weil die Variable structur in dem Beispiel ebenfalls 
uint16_t ist. Ich ging aber davon aus, daß sich dahinter dem Namen 
entsprechend auch ein struct verbirgt und kein uint16_t.

> Auf bei
> struct A {
>  uint8_t a;
>  uint8_t b;
> } structur = {};
>
> ist alles ok: man beschreibt beide Strukturkomponenten a und b. Auch
> kein UB.

Wenn man hier jetzt aber noch den eigentlichen Hack aus dem 
Ausgangsposting einfügt:

uint16_t *p;
p = (uint16_t *) structur;

Dann ist DAS an sich auch noch kein UB.

Aber wofür castet man denn auf den Pointer p, wenn man ihn eh nicht 
dereferenziert? Eben. Und sowas hier:

*p = 0;

Das ist dann undefiniertes Verhalten. Datentypkonversion durch 
Pointercasting ist nicht definiert. Der einzige Weg, wie man das in C 
machen kann, geht nicht über Pointercasting, sondern über eine Union.

von Wilhelm M. (wimalopaan)


Lesenswert?

Also man problemlos void* Zeiger casten, und man kann auch vom größeren 
Quelltyp in uint8_t* / char* casten.

Man kann aligment erzwingen / setzen -> Performanceeinbuße.

Oder man kann auch ZielTypen gleicher Größe casten.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Also man problemlos void* Zeiger casten, und man kann auch vom
> größeren Quelltyp in uint8_t* / char* casten.

Stimmt. void* kann man aber ohnehin nicht dereferenzieren. Und der 
"Trick", einen Datentyp erst nach void* zu casten und dann woanders hin, 
um das Aliasing auszutricksen, ist auch nicht erlaubt.

char* darf aber in der Tat alles und jeden aliasen. Hier ging's aber um 
uint16_t* und ein struct.

> Oder man kann auch ZielTypen gleicher Größe casten.

Nein. float und uint32_t haben beide 4 Bytes, Pointercasting geht aber 
trotzdem nicht. Dafür gibt's die union.

von Peter II (Gast)


Lesenswert?

Nop schrieb:
> Nein. float und uint32_t haben beide 4 Bytes, Pointercasting geht aber
> trotzdem nicht. Dafür gibt's die union.

nein dafür gibt es keine Union. Dafür gibt es memcpy.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:

>
> Nein. float und uint32_t haben beide 4 Bytes, Pointercasting geht aber
> trotzdem nicht. Dafür gibt's die union.

Also die union erzwingt für die member dasselbe alignmet -> ok.

Haben uint32_t und float dieselbe Größe und dasselbe alignment -> ok

Aber natürlich machen die bits des DT float in einem uint32_t wenig 
Sinn. Aber ist noch kein UB.

von W.S. (Gast)


Lesenswert?

Gast G. schrieb:
> ich hätte eine frage zu dieser Zeiger Funktion

Und ich hätte eine Gegenfrage:
Wozu brauchst du sowas?
Oder stammt das aus einem Kunststück eines anderen Schreibers?

Gerade in C-Gefilden tummeln sich Chaoten, die all die Möglichkeiten, in 
C Husarenstücke anstelle lesbaren Codes zu formulieren, als die große 
Freiheit betrachten.

Ich hatte da neulich ein klasse Beispiel bei der 
"blackmagic"-Programmieradapter-Firmware, wo jemand eine void function 
einen Returncode zurückgeben läßt. Das Erstaunliche für mich war, daß 
der verwendete Gcc dies offenbar klaglos gefressen hat.

Also, wenn das dein eigenes Produkt ist: laß sowas lieber und komme am 
besten ohne Typecasts aus. In den allermeisten Fällen kommt man ohne 
aus. Schließlich ist ein Typecast sowas wie ein "Fresse halten!" für den 
Compiler - und der bedankt sich dann dafür stillschweigend mit einem 
Verhalten des erzeugten Codes, das man garnicht erwartet hat.


W.S.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Aber ist noch kein UB.

Nicht, wenn es mit ner union gemacht wird. Sehr wohl aber UB, wenn über 
pointercasting.

von Wilhelm M. (wimalopaan)


Lesenswert?

Ich meinte oben: kein UB, wenn der Zieltyp das richtige / gleiche 
alignment hat. So steht es auch hier:

https://www.securecoding.cert.org/confluence/display/c/EXP36-C.+Do+not+cast+pointers+into+more+strictly+aligned+pointer+types

EXP36-C-EX2: If a pointer is known to be correctly aligned to the target 
type, then a cast to that type is permitted.

Also, wenn uint32_t und float beide ein 4-byte-aligment haben, sollte 
das ok sein.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Also, wenn uint32_t und float beide ein 4-byte-aligment haben, sollte
> das ok sein.

Pointer aliasing hat nichts mit alignment zu tun.

von Wilhelm M. (wimalopaan)


Lesenswert?

Ich habe nicht von aliasing (execution order constraints) gesprochen.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Ich habe nicht von aliasing (execution order constraints) gesprochen.

Ich aber bei meinem Verweis auf undefined behaviour, auf den Du reagiert 
hast.

von Nop (Gast)


Lesenswert?

W.S. schrieb:
> Also, wenn das dein eigenes Produkt ist: laß sowas lieber und komme am
> besten ohne Typecasts aus.

Vor allem bei Pointern muß man wissen, was man da tut. Kann bei 
Lowlevel-Routinen schon sinnvoll sein, ist im Allgemeinen aber ein 
Rezept ins Desaster.

von Nop (Gast)


Lesenswert?

.. und im Übrigen ist alginment auch noch plattformabhängig. Auf einem 
8-Bitter gibt's kein Alignment, und auf einem 32-bitter können auch 
uint64_t 4byte-aligned sein.

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.