Fix für Dereferenzierungstypzeiger wird strikte Aliasing brechen
-
27-10-2019 - |
Frage
Ich versuche, zwei Warnungen zu beheben, wenn ich ein bestimmtes Programm mit GCC erstellt habe. Die Warnungen sind:
WARNUNG: Derferencing typbeckener Zeiger brechen strenge Aliasing-Regeln [-wstrict-Aliasing
Und die beiden Schuldigen sind:
unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));
und
*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);
Incoming_buf und Ausgangsströmung_buf werden wie folgt definiert:
char incoming_buf[LIBIRC_DCC_BUFFER_SIZE];
char outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];
Dies scheint subtil anders zu sein als die anderen Beispiele für diese Warnung, die ich untersuchte. Ich würde es vorziehen, das Problem zu beheben, anstatt strenge Aliasing-Checks zu deaktivieren.
Es gab viele Vorschläge, eine Gewerkschaft zu verwenden - was könnte eine geeignete Gewerkschaft für diesen Fall sein?
Lösung
Lassen Sie uns zunächst untersuchen, warum Sie die Aliasing -Verstoßwarnungen erhalten.
Aliasing -Regeln Sagen Sie einfach, dass Sie nur über seinen eigenen Typ, seinen signierten / unsignierten Variantenstyp oder über einen Zeichentyp auf ein Objekt zugreifen können (über einen eigenen Typ (signiert / unsigniert).char
, signed char
, unsigned char
).
C sagt, dass Verstoß gegen Aliasing -Regeln ein undefiniertes Verhalten hervorruft (Also nicht!).
In dieser Linie Ihres Programms:
unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));
Obwohl die Elemente der incoming_buf
Array sind vom Typ char
, Sie greifen auf sie zu als auf sie zu unsigned int
. In der Tat das Ergebnis des Dereferenzoperators im Ausdruck *((unsigned int*)dcc->incoming_buf)
ist von unsigned int
Typ.
Dies ist ein Verstoß gegen die Aliasing -Regeln, da Sie nur das Recht haben, auf Elemente von zuzugreifen incoming_buf
Array durch (siehe Regeln Zusammenfassung oben!) char
, signed char
oder unsigned char
.
Beachten Sie, dass Sie in Ihrem zweiten Schuldigen genau das gleiche Aliasing -Problem haben:
*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);
Sie greifen auf die zu char
Elemente von outgoing_buf
durch unsigned int
, Es ist also ein Aliasing -Verstoß.
Vorgeschlagene Lösung
Um Ihr Problem zu beheben, können Sie versuchen, die Elemente Ihrer Arrays direkt in dem Typ zu definieren, auf den Sie zugreifen möchten:
unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
(Übrigens die Breite von unsigned int
wird die Implementierung definiert, daher sollten Sie in Betracht ziehen, die Verwendung zu verwenden uint32_t
Wenn Ihr Programm annimmt unsigned int
ist 32-Bit).
Auf diese Weise könnten Sie speichern unsigned int
Objekte in Ihrem Array, ohne gegen die Aliasing -Regeln zu verstoßen, indem Sie über den Typ auf das Element zugreifen char
, so was:
*((char *) outgoing_buf) = expr_of_type_char;
oder
char_lvalue = *((char *) incoming_buf);
BEARBEITEN:
Ich habe meine Antwort vollständig überarbeitet, insbesondere ich erkläre, warum das Programm die Aliasing -Warnungen vom Compiler erhält.
Andere Tipps
Um das Problem zu lösen, Nicht Wortspiel und Alias! Die einzige "korrekte" Möglichkeit, einen Typ zu lesen T
soll einen Typ zuweisen T
und füllen Sie seine Darstellung bei Bedarf ein:
uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);
Kurz gesagt: Wenn Sie eine Ganzzahl wünschen, müssen Sie eine Ganzzahl machen. Es gibt keine Möglichkeit, das auf sprachbezogene Weise umzugehen.
Die einzige Zeigerkonvertierung, die Sie zugelassen haben (für die Zwecke von E/A im Allgemeinen), besteht darin, die Adresse von zu behandeln eine vorhandene Variable vom Typ T
Als ein char*
, oder besser gesagt als Zeiger auf das erste Element einer Reihe von Zeichen der Größe sizeof(T)
.
union
{
const unsigned int * int_val_p;
const char* buf;
} xyz;
xyz.buf = dcc->incoming_buf;
unsigned int received_size = ntohl(*(xyz.int_val_p));
Vereinfachte Erläuterung 1. C ++ Standard gibt an, dass Sie versuchen sollten, Daten selbst auszurichten. G ++ ist eine Extrameile, um Warnungen zu diesem Thema zu generieren. 2. Sie sollten es nur versuchen, wenn Sie die Datenausrichtung Ihrer Architektur/Ihr System und in Ihrem Code vollständig verstehen (z. B. ist der obige Code auf Intel 32/64; Ausrichtung 1; Win/Linux/BSD/Mac) 3. Der einzige praktische Grund, den obigen Code zu verwenden, besteht darin, Compiler -Warnungen zu vermeiden, wann und wenn Sie wissen, was Sie tun
Wenn ich für diesen Fall, für diesen Fall, ist das Problem das Design der APIs NTOHL und HTONL sowie verwandte Funktions -APIs. Sie hätten nicht als numerisches Argument mit numerischer Rückkehr geschrieben werden dürfen. (Und ja, ich verstehe den Makrooptimierungspunkt) Sie hätten als "n" -Seite als Zeiger auf einen Puffer konzipiert werden sollen. Wenn dies erledigt ist, verschwindet das ganze Problem und die Routine ist genau, welcher Endian der Host ist. Zum Beispiel (ohne Versuch zu optimieren):
inline void safe_htonl(unsigned char *netside, unsigned long value) {
netside[3] = value & 0xFF;
netside[2] = (value >> 8) & 0xFF;
netside[1] = (value >> 16) & 0xFF;
netside[0] = (value >> 24) & 0xFF;
};
Wenn Sie Gründe haben, die Sie nicht ermöglichen, die Art des Quellobjekts zu ändern (wie in meinem Fall), und Sie sind absolut zuversichtlich, dass der Code korrekt ist und das tut, was mit diesem Char -Array zu tun hat, um Warnungen zu vermeiden, die Sie kann Folgendes tun:
unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);
Zeiger auf nicht signiert und dann zurück zum Zeiger.
unsigned int empfangen_size = ntohl ( *((nicht signiert *) ((nicht signiert) dcc-> coming_buf)));