Zeiger Funktion

#4793563
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
Gast #4793589
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.
#4793600
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 ;-)
Gast #4793763
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.
Gast #4793834
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.
#4793845
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.
Gast #4794017
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.
#4794057
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.
Gast #4794117
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren