2010-05-14 9 views
10

Heute entdeckte ich ein alarmierendes Verhalten beim Experimentieren mit Bitfeldern. Aus Gründen der Diskussion und Einfachheit, hier ist ein Beispielprogramm:GCC, -O2 und Bitfelder - ist das ein Fehler oder eine Funktion?

#include <stdio.h> 

struct Node 
{ 
    int a:16 __attribute__ ((packed)); 
    int b:16 __attribute__ ((packed)); 

    unsigned int c:27 __attribute__ ((packed)); 
    unsigned int d:3 __attribute__ ((packed)); 
    unsigned int e:2 __attribute__ ((packed)); 
}; 

int main (int argc, char *argv[]) 
{ 
    Node n; 
    n.a = 12345; 
    n.b = -23456; 
    n.c = 0x7ffffff; 
    n.d = 0x7; 
    n.e = 0x3; 

    printf("3-bit field cast to int: %d\n",(int)n.d); 

    n.d++; 

    printf("3-bit field cast to int: %d\n",(int)n.d); 
} 

Das Programm absichtlich verursacht das 3-Bit-Bit-Feld überläuft. Hier ist der (richtige) ausgegeben wird, wenn kompiliert "g ++ -O0" verwendet:

3-Bit-Feld cast INT: 7

3-Bit-Feld cast INT: 0

Hier die Ausgabe bei Verwendung von kompilierten "g ++ -O2" (und O3):

3-Bit-Feld cast iNT: 7

3-Bit-Feld Guss int: 8

den Zusammenbau des letzteren Beispiel prüfen, fand ich folgendes:

movl $7, %esi 
movl $.LC1, %edi 
xorl %eax, %eax 
call printf 
movl $8, %esi 
movl $.LC1, %edi 
xorl %eax, %eax 
call printf 
xorl %eax, %eax 
addq $8, %rsp 

Die Optimierungen "8" gerade eingefügt haben, unter der Annahme, 7 + 1 = 8, wenn in der Tat die Zahl überläuft und ist Null.

Glücklicherweise ist der Code, den ich interessiere, nicht so weit überschwemmt, wie ich weiß, aber diese Situation macht mir Angst - ist das ein bekannter Fehler, ein Feature oder ist dieses erwartete Verhalten? Wann kann ich damit rechnen, dass gcc recht hat?

Edit (re: mit/ohne Vorzeichen):

Es wird als unsigned behandelt, weil sie als unsigned deklariert wird. Deklarieren es als int Sie die Ausgabe (mit O0) erhalten:

3-Bit-Felddarsteller in int: -1

3-Bit-Feld Guss int: 0

Eine noch funnier passiert mit -O2 in diesem Fall:

3-Bit-Felddarsteller in int: 7

3-Bit-Felddarsteller in int: 8

Ich gebe zu, dass Attribut ist eine fischige Sache zu verwenden; In diesem Fall ist es ein Unterschied in den Optimierungseinstellungen, um die ich mich Sorgen mache.

+1

chedcked gcc 4.4.1 - Ausgabe ist 7/0 mit/ohne Optimierung – dimba

+0

Ich gebe zu, ich benutze 4.1.2 - danke für die Köpfe hoch. – Rooke

+0

Was passiert, wenn Sie '__attribute __ ((__ packed __))' hinter die gesamte Struktur stellen? –

Antwort

8

Wenn Sie technische Informationen erhalten möchten, die Minute, in der Sie __attribute__ (ein Bezeichner mit zwei aufeinander folgenden Unterstrichen) verwendet haben, hat/hatte Ihr Code undefiniertes Verhalten.

Wenn Sie das gleiche Verhalten mit denen entfernt bekommen, sieht es für mich wie ein Compiler-Fehler aus. Die Tatsache, dass ein 3-Bit-Feld wie 7 behandelt wird, bedeutet, dass es als unsigniert behandelt wird. Wenn Sie also überlaufen, sollte es wie alle anderen unsignierten funktionieren und Ihnen Modulo-Arithmetik geben.

Es wäre auch legitim, das Bit-Feld als signiert zu behandeln. In diesem Fall wäre das erste Ergebnis -1, -3 oder -0 (das möglicherweise nur als 0 gedruckt wird), und das zweite undefinierte (seit Überlauf einer Ganzzahl mit Vorzeichen gibt undefined Verhalten). Theoretisch könnten andere Werte unter C89 oder dem aktuellen C++ - Standard möglich sein, da sie die Repräsentationen von Ganzzahlen mit Vorzeichen nicht einschränken. In C99 oder C++ 0x können es nur diese drei sein (C99-Begrenzungen mit Vorzeichen von Ganzzahl, Zweierkomplement oder Vorzeichengröße und C++ 0x basiert auf C99 anstelle von C90).

Oops: Ich habe nicht genügend Aufmerksamkeit bezahlt - da es als unsigned definiert ist, muss es als unsigned behandelt werden, so dass wenig Spielraum übrig bleibt, um aus einem Compiler-Bug herauszukommen.

+0

Bessere Antwort, +1 – WhirlWind

+0

Das Entfernen des Attributs __ gibt auch falsches Verhalten mit -O2, aber nicht O0 (gemäß dem ursprünglichen Code). Sieht so aus, als müsste ich gcc 4.4 verwenden! – Rooke