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
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.
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.
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
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
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.
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.
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.
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.
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 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.
Wilhelm M. schrieb: > Aber ist noch kein UB. Nicht, wenn es mit ner union gemacht wird. Sehr wohl aber UB, wenn über pointercasting.
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.
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.
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.
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.
.. 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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.