Forum: Compiler & IDEs Zeiger Funktion


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Gast G. (max92)


Bewertung
0 lesenswert
nicht 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 Moderator
von René H. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Die Adresse von "structur" wird in einem uint16_t Zeiger abgelegt.

Grüsse,
René

von hicki-hacki (Gast)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht lesenswert
Ja und warum schreibst Du das dann?

von Nop (Gast)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


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

von Nop (Gast)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.