Noch einmal: strikte Aliasing-Regel und char*

Lesezeit: 7 Minuten

Benutzer-Avatar
Kiril Kirow

Je mehr ich lese, desto verwirrter werde ich.

Die letzte Frage der Verwandten kommt meiner Frage am nächsten, aber ich war verwirrt mit allen Wörtern zur Objektlebensdauer und insbesondere – ist es in Ordnung, nur zu lesen oder nicht.


Um gleich auf den Punkt zu kommen. Korrigieren Sie mich, wenn ich falsch liege.

Das ist in Ordnung, gcc gibt keine Warnung aus und ich versuche, “type T (uint32_t) über char*“:

uint32_t num = 0x01020304;
char* buff = reinterpret_cast< char* >( &num );

Aber das ist “schlecht” (gibt auch eine Warnung) und ich versuche es “umgekehrt”:

char buff[ 4 ] = { 0x1, 0x2, 0x3, 0x4 };
uint32_t num = *reinterpret_cast< uint32_t* >( buff );

Wie unterscheidet sich die zweite von der ersten, insbesondere wenn es um die Neuordnung von Anweisungen (zur Optimierung) geht? Außerdem hinzufügen const ändert nichts an der Situation.

Oder ist dies nur eine einfache Regel, die klar sagt: “In die eine Richtung geht das, in die andere nicht”? Ich konnte in den Standards nichts Relevantes finden (insbesondere im C ++ 11-Standard gesucht).

Ist dies für C und C ++ gleich (wie ich einen Kommentar gelesen habe, was bedeutet, dass es für die beiden Sprachen unterschiedlich ist)?


ich benutzte union um dies zu “workaround”, was immer noch zu sein scheint NICHT 100% OK, da es der Standard nicht garantiert (der besagt, dass ich mich nur auf den Wert verlassen kann, der zuletzt in der geändert wurde union).

Also nach dem Lesen viel, ich bin jetzt noch verwirrter. Ich vermute nur memcpy ist die “gute” Lösung?


Verwandte Fragen:

  • Was ist die strikte Aliasing-Regel?
  • Warnung “Dereferenzieren von typgesponnenen Zeigern verstößt gegen strikte Aliasing-Regeln”.
  • Verstehe ich C/C++ Strict-Aliasing richtig?
  • Strenge Aliasing-Regel und ‘char *’-Zeiger

BEARBEITEN

Die Situation in der realen Welt: Ich habe eine Drittanbieter-Lib (http://www.fastcrypto.org/), die UMAC berechnet und der zurückgegebene Wert in ist char[ 4 ]. Dann muss ich das umwandeln uint32_t. Und übrigens, die Bibliothek verwendet Dinge wie ((UINT32 *)pc->nonce)[0] = ((UINT32 *)nonce)[0] viel. Wie auch immer.

Außerdem frage ich, was richtig und was falsch ist und warum. Nicht nur über das Nachbestellen, Optimieren etc. (interessant ist das mit -O0 es gibt keine Warnungen, nur mit -O2).

Und bitte beachten: Ich bin mir der Big/Little-Endian-Situation bewusst. Das ist hier nicht der Fall. Ich möchte die Endianness hier wirklich ignorieren. Die “strengen Aliasing-Regeln” klingen nach etwas wirklich Ernstem, viel Ernsterem als falscher Endianness. Ich meine – wie das Zugreifen auf / Ändern von Speicher, der nicht berührt werden soll; irgendein Art von UB überhaupt.

Zitate aus den Normen (C und C++) wäre sehr willkommen. Ich konnte nichts über Aliasing-Regeln oder ähnliches finden.

  • Buff ist möglicherweise nicht einmal richtig ausgerichtet …

    – Marc Glisse

    30. Januar 2015 um 16:03 Uhr

  • “Wie unterscheidet sich der zweite vom ersten”, ich nehme an, Sie meinen das ausschließlich in Bezug auf Adressierung und Aliasing, da dieser Code nicht portierbar ist. Auch wenn die Ausrichtung kein Problem wäre, der Wert von num Letzterer entspricht garantiert nicht dem Anfangswert num im ersteren, es sei denn, Sie befinden sich auf einer BigE-Plattform.

    – WhozCraig

    30. Januar 2015 um 16:06 Uhr


  • @WhozCraig – Ja, ich bin mir des Big/Little Endian bewusst. Und ja, ich frage, ob es portabel und zuverlässig ist und wenn nicht – warum (ich meine, ich interessiere mich nicht nur für die Neuordnung des Codes).

    – Kiril Kirow

    30. Januar 2015 um 16:11 Uhr

  • Ich verstehe. Es ist eine großartige Frage, ich wollte nur nicht, dass der Gelegenheitsneuling das sieht und denkt, es sei eine Wunderwaffe für ihre Raw-Bytes-zu-uint32 Wehe. Uptick zu deiner Frage übrigens. Niemand, der bei Verstand ist, kann aufgrund mangelnder Recherche Ihrerseits eine Ablehnung beanspruchen.

    – WhozCraig

    30. Januar 2015 um 16:14 Uhr


  • Die Regel beginnt mit “Wenn ein Programm versucht, auf den gespeicherten Wert eines Objekts über einen anderen glvalue als einen der folgenden Typen zuzugreifen, ist das Verhalten undefiniert: […]”. In Ihrem ersten Fall ist das “Objekt” a uint32_t und Sie greifen über einen glvalue vom Typ darauf zu char, was erlaubt ist; In Ihrem zweiten Fall ist das “Objekt” entweder a char oder eine Reihe von chars, und Sie greifen über einen glvalue vom Typ darauf zu uint32_twas keiner der zulässigen Typen ist.

    – TC

    30. Januar 2015 um 16:31 Uhr

Benutzer-Avatar
davmac

Wie unterscheidet sich die zweite von der ersten, insbesondere wenn es um die Neuordnung von Anweisungen (zur Optimierung) geht?

Das Problem liegt darin, dass der Compiler anhand der Regeln bestimmt, ob eine solche Optimierung zulässig ist. Im zweiten Fall versuchen Sie, a zu lesen char[] Objekt über einen inkompatiblen Zeigertyp, was ein undefiniertes Verhalten ist; Daher kann der Compiler das Lesen und Schreiben neu ordnen (oder etwas anderes tun, was Sie möglicherweise nicht erwarten).

Aber es gibt Ausnahmen für den “anderen Weg”, dh das Lesen eines Objekts eines bestimmten Typs über einen Zeichentyp.

Oder ist dies nur eine einfache Regel, die klar sagt: “In die eine Richtung geht das, in die andere nicht”? Ich konnte in den Standards nichts Relevantes finden (insbesondere im C ++ 11-Standard gesucht).

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf Kapitel 3.10 Absatz 10.

In C99 und auch in C11 ist es 6.5 Absatz 7. Für C++11 ist es 3.10 (“Lvalues ​​and Rvalues”).

Sowohl C als auch C++ erlauben den Zugriff auf jeden Objekttyp über char * (oder speziell ein lvalue des Zeichentyps für C oder beides unsigned char oder char Typ für C++). Sie erlauben keinen Zugriff auf a char Objekt über einen beliebigen Typ. Also ja, die Regel ist eine “Einbahnstraße”.

Ich habe Union verwendet, um dies zu “umgehen”, was immer noch NICHT 100% OK zu sein scheint, da es nicht durch den Standard garantiert ist (der besagt, dass ich mich nur auf den Wert verlassen kann, der zuletzt in der Union geändert wurde).

Obwohl der Wortlaut des Standards schrecklich zweideutig ist, ist es in C99 (und darüber hinaus) klar (zumindest seit C99 TC3), dass die Absicht ist es, Typ-Wortspiel durch eine Union zu ermöglichen. Sie müssen jedoch alle Zugriffe über die Union durchführen. Es ist auch nicht klar, dass Sie eine Union “in Existenz umwandeln” können, dh das Union-Objekt muss zuerst existieren, bevor Sie es für das Typ-Wortspiel verwenden.

der zurückgegebene Wert ist in char[ 4 ]. Dann muss ich dies in uint32_t konvertieren

Benutz einfach memcpy oder verschieben Sie die Bytes manuell an die richtige Position, falls die Byte-Reihenfolge ein Problem darstellt. Gute Compiler können das trotzdem optimieren (ja, sogar der Aufruf von memcpy).

  • In beiden Fällen werden “inkompatible Zeigertypen” verwendet. Also, Sie sagen, dass die Ausnahme etwa char* ist nur für den einen weg und nicht für den anderen?

    – Kiril Kirow

    30. Januar 2015 um 16:26 Uhr

Ich habe Union verwendet, um dies zu “umgehen”, was immer noch NICHT 100% OK zu sein scheint, da es nicht durch den Standard garantiert ist (der besagt, dass ich mich nur auf den Wert verlassen kann, der zuletzt in der Union geändert wurde).

Endianess ist der Grund dafür. Insbesondere die Folge von Bytes 01 00 00 00 könnte 1 oder 16.777.216 bedeuten.

Der richtige Weg, das zu tun, was Sie tun, besteht darin, nicht mehr zu versuchen, den Compiler dazu zu bringen, die Konvertierung für Sie durchzuführen, und die Konvertierung selbst durchzuführen.

Wenn zum Beispiel die char[4] Little-Endian ist (kleinstes Byte zuerst), dann würden Sie etwa Folgendes tun.

char[] buff = new char[4];
uint32_t result = 0;
for (int i = 0; i < 4; i++)
    result = (result << 8) + buff[i];

Dies führt die Konvertierung zwischen den beiden manuell durch und ist garantiert immer korrekt, wenn Sie die mathematische Konvertierung durchführen.

Wenn Sie diese Konvertierung jetzt schnell durchführen, könnte es sinnvoll sein, #if und Kenntnisse Ihrer Architektur zu verwenden, um eine Aufzählung zu verwenden, um dies automatisch zu tun, wie Sie erwähnt haben, aber das ist wieder eine Abkehr von tragbaren Lösungen. (Sie können auch so etwas als Fallback verwenden, wenn Sie sich nicht sicher sein können.)

1098840cookie-checkNoch einmal: strikte Aliasing-Regel und char*

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy