Das Dereferenzieren eines typgesponnenen Zeigers verstößt gegen strikte Aliasing-Regeln
Lesezeit: 6 Minuten
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
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
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
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
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?
14326300cookie-checkDas Dereferenzieren eines typgesponnenen Zeigers verstößt gegen strikte Aliasing-Regelnyes
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
NULL
nichtnull
.– 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