Das Dereferenzieren eines typgesponnenen Zeigers verstößt gegen strikte Aliasing-Regeln

Lesezeit: 6 Minuten

Benutzeravatar von liv2hak
liv2hak

Ich habe einen vorzeichenlosen Zeichenzeiger, der eine Struktur enthält. Jetzt möchte ich Folgendes tun

unsigned char buffer[24];

//code to fill the buffer with the relevant information.

int len = ntohs((record_t*)buffer->len);

wo record_t Struktur enthält ein Feld namens len. Ich kann das nicht und erhalte den Fehler.

error: request for member ‘len’ in something not a structure or union.

Dann habe ich versucht:

int len = ntohs(((record_t*)buffer)->len);

um die Operatorpriorität richtig zu bekommen. Das gab mir:

Warnung: Das Dereferenzieren von typgesponnenen Zeigern verstößt gegen strikte Aliasing-Regeln.

dann habe ich erklärt

record_t *rec = null;

rec = (record_t*)

was mache ich hier falsch?

  • Was sind Ihre Compiler-Optionen? Konzentrieren Sie sich außerdem auf das vorliegende Problem und teilen Sie uns keine früheren Syntaxfehler mit, auf die Sie gestoßen sind.

    – Kerrek SB

    3. Oktober 2011 um 0:26 Uhr


  • In C ist es NULLnicht null.

    – Chris Lutz

    3. Oktober 2011 um 1:01 Uhr

  • Mögliches Duplikat von Was ist die strikte Aliasing-Regel?

    – Sneftel

    25. April 2017 um 9:13 Uhr

Benutzeravatar von Kerrek SB
Kerrek SB

Gemäß den C- und C++-Standards ist dies der Fall undefiniertes Verhalten um auf eine Variable eines bestimmten Typs über einen Zeiger auf einen anderen Typ zuzugreifen. Beispiel:

int a;
float * p = (float*)&a;  // #1
float b = *p;            // #2

Hier verursacht #2 undefiniertes Verhalten. Die Zuweisung bei Nr. 1 wird als “Typ-Wortspiel” bezeichnet. Der Begriff “Aliasing” bezieht sich auf die Idee, dass mehrere verschiedene Zeigervariablen auf dieselben Daten zeigen können – in diesem Fall p aliasiert die Daten a. Legales Aliasing ist ein Problem für die Optimierung (was einer der Hauptgründe für die überlegene Leistung von Fortran in bestimmten Situationen ist), aber was wir hier haben, ist eine Vollversion illegal Aliasing.

Ihre Situation ist nicht anders; Sie greifen auf Daten zu buffer durch einen Zeiger auf einen anderen Typ (dh einen Zeiger, der keiner ist unsigned char *). Das ist einfach nicht erlaubt.

Das Fazit ist: Du hättest nie Daten bei haben sollen buffer an erster Stelle.

Aber wie löst man es? Stellen Sie sicher, dass Sie einen gültigen Zeiger haben! Es gibt eine Ausnahme vom Wortspiel, nämlich der Zugriff auf Daten über einen Zeiger auf char, was ist erlaubt. Also können wir das schreiben:

record_t data;
record_t * p = &data;          // good pointer
char * buffer = (char*)&data;  // this is allowed!

return p->len;                 // access through correct pointer!

Der entscheidende Unterschied besteht darin, dass wir die echten Daten in einer Variablen des richtigen Typs speichern und erst nachdem wir diese Variable zugewiesen haben, behandeln wir die Variable als ein Array von Zeichen (was zulässig ist). Die Moral hier ist, dass das Zeichenarray immer an zweiter Stelle steht und der echte Datentyp an erster Stelle steht.

  • +1 dafür, dass Sie OP nicht nur sagen, was falsch ist, sondern zeigen der richtige Weg um dies zu tun!

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    3. Oktober 2011 um 3:21 Uhr

  • Nein, nein, Sie übertreiben und verfehlen den Punkt. Zugriff auf ein Objekt über einen anderen Typ kann undefiniertes Verhalten sein, wenn dies zB zu einer Trap-Darstellung oder Fehlausrichtung führt, muss es aber nicht. Der Punkt des Aliasing ist völlig anders. Denn der Standard erlaubt dem Compiler anzunehmen, dass sich Zeiger unterschiedlichen Typs nicht gegenseitig aliasen und dass er daher möglicherweise aggressivere Optimierungen durchführt, die dann schief gehen können, wenn sie in Wirklichkeit auf dasselbe Objekt zeigen.

    – Jens Gustedt

    3. Oktober 2011 um 6:39 Uhr

  • @JensGustedt: Ich glaube nicht, dass die Ausrichtung in den Regeln eine Rolle spielt. Sie können hin und her werfen, das ist in Ordnung (wie T * p = &x; S * q = (S*)p; T * r = (T*)q; T y = *r;), aber ansonsten ist es nur ein undefiniertes Verhalten (das sich wie erwartet verhalten kann). Aliasing ist ein breites Konzept (betrachten Sie z. B. den “Vektoraddierer” add(float * a, float * b, float * c) wo es nützlich ist zu wissen, ob a oder b alias c), aber die “strikte Aliasing-Regel” besagt, dass Zeiger unterschiedlicher Typen sich niemals gegenseitig Aliasnamen geben können.

    – Kerrek SB

    3. Oktober 2011 um 10:16 Uhr

Benutzeravatar von Mystical
Mystisch

Sie erhalten diese Warnung, weil Sie das strikte Aliasing brechen, indem Sie zwei Zeiger unterschiedlichen Typs haben, die auf denselben Ort zeigen.

Eine Möglichkeit, dies zu umgehen, besteht darin, Gewerkschaften zu verwenden:

union{
    unsigned char buffer[24];
    record_t record_part;
};

//code to fill the buffer with the relavent information.

int len = ntohs(record_part.len);

BEARBEITEN:

Genau genommen ist dies nicht viel sicherer als Ihr ursprünglicher Code, aber es verstößt nicht gegen striktes Aliasing.

  • Der Zugriff auf eine Union außerhalb der Reihenfolge ist ein undefiniertes Verhalten. Sie “kommen um nichts herum” (aber einen direkten Weg zum Grab).

    – Kerrek SB

    3. Oktober 2011 um 0:34 Uhr


  • @Kerrek SB: Können Sie erklären, was Sie mit “außer Betrieb” meinen? Ja, ich weiß, dass der Union-Ansatz ebenfalls undefiniert ist, aber das ist auch beim ursprünglichen Code der Fall.

    – Mystisch

    3. Oktober 2011 um 0:37 Uhr


  • Es darf nur das gleiche Gewerkschaftsmitglied gelesen werden, das zuletzt angeschrieben wurde. Für Ihre Lösung wäre es jedoch mit ziemlicher Sicherheit erforderlich, ein anderes Mitglied zu lesen. Es gibt keine Möglichkeit, Wortspiele mit ungültigen Typen zu umgehen; du wirklich haben um mit einer Variablen des richtigen Typs zu beginnen (siehe meine Antwort).

    – Kerrek SB

    3. Oktober 2011 um 0:40 Uhr


  • @Mystical, es sollte wahrscheinlich sein: int len ​​= ntohs(record_part.len);

    – Jim Rhodes

    3. Oktober 2011 um 0:41 Uhr

  • @AndreyT – C99 TC3 macht es legal, mit Gewerkschaften Wortspiele durchzuführen. (Ich kann es nicht zitieren, aber überprüfen Sie stackoverflow.com/questions/7411233/… für einige Leute, die mir zustimmen.)

    – Chris Lutz

    3. Oktober 2011 um 1:22 Uhr

Benutzeravatar von Jim Rhodes
Jim Rhodes

Sie könnten dies versuchen:

unsigned char buffer[sizeof(record_t)];
record_t rec;
int len;

// code to fill in buffer goes here...

memcpy(&rec, buffer, sizeof(rec));
len = ntohs(rec.len);

  • Dies ist eigentlich der richtige Weg, es zu tun (casts to void * sind aber unnötig). Der richtige Weg, Daten eines Typs als Daten eines anderen Typs neu zu interpretieren, besteht darin, diese Daten mithilfe von zwischen Objekten zu kopieren memcpy (anstelle einer direkten zeigerbasierten Neuinterpretation oder unionsbasierten Neuinterpretation).

    – AnT steht zu Russland

    3. Oktober 2011 um 1:17 Uhr

  • Ich stimme zu, dass dies richtig ist (und ich habe ihm +1 gegeben), aber es wäre besser, es niemals zu verwenden unsigned char[] zu beginnen und die Daten direkt in eine Variable des richtigen Typs einzulesen.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    3. Oktober 2011 um 3:24 Uhr

Benutzeravatar von Foo Bah
Foo Bah

Sie haben wahrscheinlich eine Warnstufe eingestellt, die strikte Aliasing-Warnungen enthält (früher war dies nicht die Standardeinstellung, aber an einem Punkt hat gcc die Standardeinstellung umgedreht). Versuchen -Wno-strict-aliasing oder -fno-strict-aliasing — dann sollte gcc keine Warnungen generieren

Eine ziemlich gute Erklärung (basierend auf einem flüchtigen Blick) ist Was ist die strikte Aliasing-Regel?

1432630cookie-checkDas Dereferenzieren eines typgesponnenen Zeigers verstößt gegen strikte Aliasing-Regeln

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

Privacy policy