2010-05-14 16 views
10

Oggi ho scoperto un comportamento allarmante durante la sperimentazione con campi di bit. Per motivi di discussione e di semplicità, ecco un esempio di programma:GCC, -O2 e bitfield: si tratta di un bug o di una funzionalità?

#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); 
} 

Il programma è volutamente la causa del campo di bit 3 bit per traboccare. Ecco il (corretto) uscita quando elaborato applicando "g ++ -O0":

campo Cast 3 bit a int: 7

campo Cast 3 bit a int: 0

Ecco l'uscita quando compilato utilizzando "g ++ -O2" (e -O3):

campo Cast 3 bit a int: 7

Cast campo 3-bit int: 8

Controllo del montaggio di quest'ultimo esempio, ho trovato questo:

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 

Le ottimizzazioni hanno appena inserito "8", assumendo 7 + 1 = 8 mentre in realtà il numero trabocca ed è zero.

Fortunatamente il codice che mi interessa non esagera per quanto ne so, ma questa situazione mi spaventa: si tratta di un bug noto, di una funzionalità o di un comportamento previsto? Quando posso aspettarmi che gcc abbia ragione su questo?

Edit (Re: firmato/unsigned):

E 'trattato come senza segno, perché è dichiarato come non firmato. Dichiarandolo come int si ottiene l'uscita (con O0):

campo getto 3-bit INT: -1

campo getto 3-bit INT: 0

An anche cosa divertente accade con -O2 in questo caso:

campo Cast 3 bit a int: 7

campo Cast 3 bit a int: 8

Ammetto che l'attributo è una cosa di pesce da usare; in questo caso è una differenza nelle impostazioni di ottimizzazione di cui sono preoccupato.

+1

chedcked gcc 4.4.1 - l'output è 7/0 con/senza ottimizzazione – dimba

+0

Ammetto che sto usando 4.1.2 - grazie per l'heads up. – Rooke

+0

Cosa succede se metti '__attribute __ ((__ imballato __))' dopo l'intera struttura? –

risposta

8

Se si desidera ottenere informazioni tecniche, nel momento in cui è stato utilizzato __attribute__ (un identificativo contenente due caratteri di sottolineatura consecutivi) il codice ha/ha avuto un comportamento non definito.

Se si ottiene lo stesso comportamento con quelli rimossi, mi sembra un bug del compilatore. Il fatto che un campo a 3 bit venga trattato come 7 significa che viene trattato come un unsigned, quindi quando si esegue un overflow dovrebbe fare come qualsiasi altro unsigned e fornire l'aritmetica del modulo.

Sarebbe anche legittimo per esso trattare il campo di bit come firmato. In questo caso, il primo risultato sarà -1, -3 o -0 (che potrebbe essere stampato solo come 0) e il secondo non definito (poiché l'overflow di un intero con segno fornisce un comportamento non definito). In teoria, altri valori potrebbero essere possibili con C89 o lo standard C++ corrente poiché non limitano le rappresentazioni di interi con segno. In C99 o C++ 0x, possono essere solo questi tre (C99 limita gli interi con segno al complemento a uno, il complemento a due o il segno-grandezza e C++ 0x è basato su C99 anziché C90).

Ops: non ho prestato abbastanza vicino attenzione - dal momento che è definita come unsigned, deve essere trattato come unsigned, lasciando poco spazio di manovra per uscire del suo essere un errore del compilatore.

+0

Risposta migliore, +1 – WhirlWind

+0

La rimozione di __attribute __ ha anche un comportamento errato con -O2, ma non O0 (come per il codice originale). Sembra che ho bisogno di usare gcc 4.4! – Rooke

Problemi correlati