2008-11-04 6 views
23

Ich habe versucht, die strengen Aliasing-Regeln zu verstehen, wie sie für den Char-Zeiger gelten.Wann ist char * sicher für striktes Pointer-Aliasing?

Here dies angegeben:

Es wird immer davon ausgegangen, dass ein char * zu einem Aliasnamen eines Objekts bezieht.

Ok so im Zusammenhang mit der Buchse Code, kann ich dies tun:

struct SocketMsg 
{ 
    int a; 
    int b; 
}; 

int main(int argc, char** argv) 
{ 
    // Some code... 
    SocketMsg msgToSend; 
    msgToSend.a = 0; 
    msgToSend.b = 1; 
    send(socket, (char*)(&msgToSend), sizeof(msgToSend); 
}; 

Aber dann gibt es diese Aussage

Das Gegenteil ist nicht wahr. Das Übergeben eines char * an einen Zeiger eines anderen Typs als char * und das Dereferenzieren dieser Eigenschaft widerspricht normalerweise der strengen Aliasing-Regel.

Bedeutet dies, dass, wenn ich ein Char-Array empf ich nicht gegossen auf eine Struktur neu interpretieren kann, wenn ich die Struktur der Nachricht kennen:

struct SocketMsgToRecv 
{ 
    int a; 
    int b; 
}; 

int main() 
{ 
    SocketMsgToRecv* pointerToMsg; 
    char msgBuff[100]; 
    ... 
    recv(socket, msgBuff, 100); 
    // Ommiting make sure we have a complete message from the stream 
    // but lets assume msgBuff[0] has a complete msg, and lets interpret the msg 

    // SAFE!?!?!? 
    pointerToMsg = &msgBuff[0]; 

    printf("Got Msg: a: %i, b: %i", pointerToMsg->a, pointerToMsg->b); 
} 

Wird dieses zweite Beispiel nicht funktionieren, weil die Basistyp ist ein Char-Array und ich werfe es auf eine Struktur? Wie gehst du mit dieser Situation in einer genau definierten Welt um?

+0

Erfordert der zweite Teil des Codes nicht das explizite Übergeben? Hast du alle Warnungen aktiviert? –

Antwort

6

Korrekt, das zweite Beispiel verstößt gegen die strengen Aliasing-Regeln. Wenn Sie also mit dem Flag -fstrict-aliasing kompilieren, besteht die Möglichkeit, dass Sie einen falschen Objektcode erhalten. Die voll richtige Lösung wäre hier eine Vereinigung zu nutzen:

union 
{ 
    SocketMsgToRecv msg; 
    char msgBuff[100]; 
}; 

recv(socket, msgBuff, 100); 

printf("Got Msg: a: %i, b: %i", msg.a, msg.b); 
+1

Ist dies in Übereinstimmung mit dem Standard oder nur Compiler lassen Sie mit Schreiben zu einem Mitglied und Lesen von einem anderen Mitglied? –

+9

Die Vereinigung ist völlig unnötig. Übergeben Sie einfach einen Zeiger auf die Struktur (Cast in 'char *') zu 'recv'. –

+0

Beachten Sie, dass '-fstrict-aliasing' standardmäßig auf' -O2' und höher in gcc gesetzt ist –

7

Re @ Adam Rosenfield: Die Gewerkschaft wird die Ausrichtung erreichen, solange der Lieferant des char * begann ähnlich, etwas zu tun.

Es kann nützlich sein, sich zurückzuziehen und herauszufinden, worum es geht. Die Basis für die Aliasing-Regel ist die Tatsache, dass Compiler Werte verschiedener einfacher Typen auf verschiedenen Speichergrenzen platzieren können, um den Zugriff zu verbessern, und dass Hardware in einigen Fällen eine solche Ausrichtung erforderlich machen kann, um den Zeiger überhaupt verwenden zu können. Dies kann auch in Strukturen angezeigt werden, in denen eine Vielzahl von Elementen unterschiedlicher Größe vorhanden ist. Die Struktur kann an einer guten Grenze beginnen. Darüber hinaus kann der Compiler im Inneren der Struktur noch Lücken einführen, um die richtige Ausrichtung der Strukturelemente zu erreichen, die sie benötigen.

Wenn man bedenkt, dass Compiler oft Optionen zur Kontrolle haben, wie all dies gehandhabt wird oder nicht, kann man sehen, dass es viele Möglichkeiten gibt, wie Überraschungen auftreten können. Dies ist besonders wichtig bei der Übergabe von Zeigern an Strukturen (in Chars * oder nicht) in Bibliotheken, die kompiliert wurden, um unterschiedliche Ausrichtungskonventionen zu erwarten.

Was ist mit Char *?

Die Annahme über char * ist die Größe von (char) == 1 (relativ zu den Größen aller anderen großen Daten) und dass char * Pointer keine Ausrichtungsanforderungen haben. So kann ein echtes char * immer sicher herumgereicht und erfolgreich verwendet werden, ohne sich um die Ausrichtung zu kümmern, und das gilt für jedes Element eines char [] -Arrays, das ++ und - auf den Zeigern ausführt und so weiter. (Seltsam, void * ist nicht ganz dasselbe.)

Jetzt sollten Sie in der Lage zu sehen, wenn Sie eine Art von Strukturdaten in ein char [] -Array, die nicht selbst ausgerichtet wurde, um einen Zeiger, der Alignment (s) erforderlich sein kann zurückwerfen ein ernstes Problem.

Wenn Sie eine Union aus einem Array char [] und einer Struktur erstellen, wird die anspruchsvollste Ausrichtung (d. H. Die der Struktur) vom Compiler berücksichtigt. Dies wird funktionieren, wenn der Anbieter und der Verbraucher effektiv übereinstimmende Vereinigungen verwenden, so dass das Umwandeln des struct * in char * und zurück einwandfrei funktioniert.

In diesem Fall würde ich hoffen, dass die Daten in einer ähnlichen Union erstellt wurden, bevor der Zeiger auf char * umgewandelt wurde, oder es wurde anders als ein Array von sizeof (char) Bytes übertragen. Es ist auch wichtig sicherzustellen, dass alle Compiler-Optionen kompatibel sind zwischen den verwendeten Bibliotheken und Ihrem eigenen Code.

+0

sind alle 3 von 'char',' signed char', 'unsigned char' OK für Aliasing? und mit jeder CV-Qualifikationskombination? –

+2

Die Alias-Regeln haben nichts mit Alignment zu tun. Gemäß der C89-Logik, bei globalen Deklarationen wie "int i; float * fp; ', der Zweck besteht darin, Compilern zu erlauben,' i' in einem Register über Zugriffe auf '* fp' zu halten. Die Idee war, dass ein Compiler nicht pessimistisch davon ausgehen sollte, dass ein Schreiben in '* fp '' i' * ändern könnte, wenn kein Grund zu der Annahme bestand, dass '* fp' auf etwas zeigen würde, das kein' 'ist schweben' *. Ich glaube nicht, dass die Regel jemals dazu gedacht war, dass Compiler Fälle ignorieren sollten, in denen das Aliasing offensichtlich ist (die Adresse eines Objekts sollte dem Compiler einen starken Hinweis geben ... – supercat

+0

... dass auf das betreffende Objekt zugegriffen wird Über Pointer, und ein 'int *' zu einem 'float *' zu werfen, sollte dem Compiler einen starken Hinweis geben, dass ein 'int' wahrscheinlich durch Schreiben in ein' float * 'modifiziert wird, aber gcc fühlt sich dazu nicht mehr verpflichtet notieren Sie solche Dinge – supercat