Fix für die Dereferenzierung von typgesponnenen Zeigern unterbricht Strict-Aliasing

Lesezeit: 6 Minuten

Benutzeravatar von BlankFrank
BlankFrank

Ich versuche, zwei Warnungen zu beheben, wenn ich ein bestimmtes Programm mit GCC kompiliere. Die Warnungen lauten:

Warnung: Das Dereferenzieren von typgesponnenen Zeigern verstößt gegen strikte Aliasing-Regeln [-Wstrict-aliasing]

und die zwei Übeltäter sind:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

und

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);

eingehende_buf und ausgehende_buf sind wie folgt definiert:

char                    incoming_buf[LIBIRC_DCC_BUFFER_SIZE];

char                    outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];

Dies scheint sich subtil von den anderen Beispielen dieser Warnung zu unterscheiden, die ich untersucht habe. Ich würde es vorziehen, das Problem zu beheben, anstatt Strict-Aliasing-Prüfungen zu deaktivieren.

Es gab viele Vorschläge zur Verwendung einer Verbindung – welche Verbindung könnte für diesen Fall geeignet sein?

  • Interessant … Strict-Aliasing sollte nicht gelten char*. Oder übersehe ich etwas?

    – Mystisch

    11. Januar 2012 um 18:30 Uhr

  • @Mystcial Ja, was Sie vermissen, ist, dass es bei einem Objekt des Typs keine Aliasing-Verletzung gibt T1 wird mit einem lvalue vom Typ zugegriffen T2 und T2 ist charaber wenn T1 ist char und T2 ist keine signierte/unsignierte Variante von charliegt eine Aliasing-Verletzung vor.

    – au

    11. Januar 2012 um 19:08 Uhr

  • @Mystical: Du hast es falsch herum verstanden!

    – Kerrek SB

    11. Januar 2012 um 19:14 Uhr

Benutzeravatar von ouah
ouah

Lassen Sie uns zunächst untersuchen, warum Sie die Aliasing-Verletzungswarnungen erhalten.

Aliasing-Regeln Sagen Sie einfach, dass Sie auf ein Objekt nur über seinen eigenen Typ, seinen signierten/unsignierten Variantentyp oder über einen Zeichentyp (char, signed char, unsigned char).

C sagt, dass das Verletzen von Aliasing-Regeln undefiniertes Verhalten hervorruft (also nicht!).

In dieser Zeile Ihres Programms:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

obwohl die Elemente der incoming_buf array sind vom Typ chargreifen Sie darauf zu als unsigned int. Tatsächlich das Ergebnis des Dereferenzierungsoperators 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 Regelzusammenfassung oben!) char, signed char oder unsigned char.

Beachten Sie, dass Sie bei Ihrem zweiten Täter 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 intes handelt sich also um eine Aliasing-Verletzung.

Vorgeschlagene Lösung

Um Ihr Problem zu beheben, könnten 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 ist die Implementierung definiert, daher sollten Sie die Verwendung von in Betracht ziehen uint32_t wenn Ihr Programm davon ausgeht unsigned int ist 32-Bit).

Auf diese Weise könnten Sie speichern unsigned int Objekte in Ihrem Array, ohne die Aliasing-Regeln zu verletzen, indem Sie über den Typ auf das Element zugreifen charso was:

*((char *) outgoing_buf) =  expr_of_type_char;

oder

char_lvalue = *((char *) incoming_buf);

BEARBEITEN:

Ich habe meine Antwort komplett überarbeitet, insbesondere erkläre ich, warum das Programm die Aliasing-Warnungen vom Compiler erhält.

  • Die Warnung kann durch doppeltes Durchwerfen zum Schweigen gebracht werden void *. siehe meine antwort

    – yanychar

    17. Mai um 16:46 Uhr

Um das Problem zu lösen, kein Wortspiel und Alias! Die einzig “richtige” Art, einen Typ zu lesen T ist die Zuordnung eines Typs T und füllen Sie seine Darstellung bei Bedarf aus:

uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);

Kurz gesagt: Wenn Sie eine ganze Zahl wollen, müssen Sie eine ganze Zahl machen. Es gibt keine Möglichkeit, das sprachlich geduldet zu umgehen.

Die einzige Zeigerkonvertierung, die Ihnen erlaubt ist (im Allgemeinen für E/A-Zwecke), besteht darin, die Adresse von zu behandeln eine vorhandene Variable des Typs T Als ein char*oder besser gesagt, als Zeiger auf das erste Element eines Arrays von Zeichen der Größe sizeof(T).

  • Ich bin mir nicht sicher sizeof(uint32_t) ist garantiert 4, daher müssen Sie möglicherweise Ihre anpassen memcpy

    – OmarL

    24. August 2020 um 15:27 Uhr

  • @OmarL: richtig. uint32_t hat garantiert genau 32 Wertbits und keine Füllbits, aber sizeof(uint32_t) kann sein 2 wenn unsigned char hat 16 Bit und sogar 1 wenn unsigned char hat 32 Bit. Das Beispiel sollte wie folgt geändert werden memcpy(&n, dcc->incoming_buf, sizeof(n));

    – chqrlie

    17. Mai um 17:19 Uhr

Benutzeravatar von Real Name
Echter Name

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 Erklärung 1. Der c++-Standard besagt, dass Sie versuchen sollten, Daten selbst auszurichten, g++ geht noch einen Schritt weiter, um Warnungen zu diesem Thema zu generieren. 2. Sie sollten es nur versuchen, wenn Sie die Datenausrichtung auf Ihrer Architektur/Ihrem System und in Ihrem Code vollständig verstehen (zum Beispiel ist der obige Code eine sichere Sache 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, WENN und WENN Sie wissen, was Sie tun

Benutzeravatar von Henri Socha
Henri Socha

Wenn ich darf, ist das Problem meiner Meinung nach in diesem Fall das Design der ntohl- und htonl- und verwandten Funktions-APIs. Sie sollten nicht als numerisches Argument mit numerischer Rückgabe geschrieben werden. (und ja, ich verstehe den Punkt der Makrooptimierung) Sie sollten so konzipiert sein, dass die ‘n’-Seite ein Zeiger auf einen Puffer ist. Wenn dies erledigt ist, verschwindet das ganze Problem und die Routine ist genau, egal welches Endian der Host ist. Zum Beispiel (ohne Optimierungsversuch):

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 es Ihnen nicht erlauben, den Typ des Quellobjekts zu ändern (wie es in meinem Fall war), und Sie absolut sicher sind, dass der Code korrekt ist und das tut, was mit diesem Zeichenarray beabsichtigt ist, vermeiden Sie Warnungen kann Folgendes tun:

unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);

  • Warnungen wegzuwerfen ist schlecht. Dieser Code tut nichts gegen striktes Aliasing und kann auch Ausrichtungsbeschränkungen verletzen.

    – Andreas Henle

    20. April 2021 um 14:42 Uhr

Benutzeravatar von DosMan
DosMan

Ich habe kürzlich ein Projekt von GCC 6 auf GCC 9 aktualisiert und diese Warnung wurde angezeigt. Das Projekt befindet sich auf einem 32-Bit-Mikrocontroller, und ich hatte eine Struktur erstellt, um auf die einzelnen Bytes eines 32-Bit-Maschinenregisters zuzugreifen:

struct TCC_WEXCTRL_t
{
    byte    OTMX;
    byte    DTIEN;
    byte    DTLS;
    byte    DTHS;
};

und dann codiert:

((TCC_WEXCTRL_t *)&TCC0->WEXCTRL)->DTLS = PwmLoDeadTime;

was die Warnung im neuen Compiler erzeugte. Ich fand heraus, dass ich die Warnung beseitigen konnte, indem ich meine Struktur in einer Vereinigung mit dem ursprünglichen Typ kombinierte:

union TCC_WEXCTRL_t
{
    TCC_WEXCTRL_Type std;
    struct  
    {
        byte    OTMX;
        byte    DTIEN;
        byte    DTLS;
        byte    DTHS;
    };    
};

wo TCC_WEXCTRL_Type ist die Art der WEXCTRL wie in den Header-Dateien des Herstellers angegeben.

Ich bin mir nicht sicher, ob dies als vollständig konformer Fix angesehen wird oder ob GCC es einfach nicht erkennt. Wenn dies nicht funktioniert hätte (oder in einem anderen GCC-Upgrade hängen geblieben wäre), würde ich dazu übergehen, eine Vereinigung der Zeigertypen zu verwenden, wie in diesem Thread von Real Name beschrieben.

  • Warnungen wegzuwerfen ist schlecht. Dieser Code tut nichts gegen striktes Aliasing und kann auch Ausrichtungsbeschränkungen verletzen.

    – Andreas Henle

    20. April 2021 um 14:42 Uhr

Benutzeravatar von Evgeny Yashin
Jewgeni Jaschin

C Cast hat nicht funktioniert, aber reinterpret_cast<> hat mir in einer ähnlichen Situation geholfen.

1403770cookie-checkFix für die Dereferenzierung von typgesponnenen Zeigern unterbricht Strict-Aliasing

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

Privacy policy