2012-07-01 20 views
9

de trabajo en C11, la siguiente estructura:Un error en la aplicación GCC de campos de bits

struct S { 
    unsigned a : 4; 
    _Bool b : 1; 
}; 

consigue organizada por GCC como un unsigned (4 bytes) de los que se utilizan 4 bits, seguido de un _Bool (4 bytes) de los cuales se usa 1 bit, para un tamaño total de 8 bytes.

Tenga en cuenta que C99 y C11 permiten específicamente _Bool como miembro de campo de bits. El estándar C11 (C99 y probablemente también) También Unidos bajo §6.7.2.1 'Estructura y unión especificadores' ¶ 11 que:

Una implementación puede asignar cualquier unidad de almacenamiento direccionable lo suficientemente grande como para contener un campo de bits. Si queda suficiente espacio, un campo de bits que sigue inmediatamente a otro campo de bits en una estructura debe empaquetarse en los bits adyacentes de la misma unidad.

Así que creen que el miembro de b anteriormente debería haber sido embalado en la unidad de almacenamiento asignado para el miembro a, resultando en una estructura de tamaño total de 4 bytes.

GCC se comporta correctamente y el embalaje se produce cuando se utilizan los mismos tipos de los dos miembros, o cuando uno es unsigned y el otro signed, pero los tipos unsigned y _Bool parecen estar considerado demasiado distinta por GCC para que pueda manejarlos correctamente.

¿Alguien puede confirmar mi interpretación de la norma, y ​​que esto es de hecho una falla de GCC?

También estoy interesado en una solución temporal (algunos modificador del compilador, pragma, __attribute__ ...).

estoy usando gcc 4.7.0 con -std=c11 (aunque otras configuraciones muestran el mismo comportamiento.)

+0

Tenga en cuenta que la extensión GCC '__attribute__ ((packed))' se puede aplicar a los miembros aquí , pero es ortogonal a este problema (resulta en una estructura de tamaño 4 + 1 = 5, es decir, con el mismo problema). – ndkrempel

+0

Relacionados: http://stackoverflow.com/questions/308364/c-bitfield-packing-with -bools (pero se refiere a C++, que no es tan exacto en su redacción en campos de bits.) – ndkrempel

+0

Según una respuesta a la pregunta vinculada anteriormente, este comportamiento no se produjo en gcc 4.2.4, por lo que puede ser una regresión desde entonces. – ndkrempel

Respuesta

10

El comportamiento descrito es incompatible con las normas de C99 y C11, pero se proporciona por compatibilidad binaria con el compilador MSVC (que tiene un comportamiento inusual estructura de embalaje.)

Afortunadamente, se puede desactivar, ya sea en el código con __attribute__((gcc_struct)) aplicado a la estructura o con el modificador de línea de comandos -mno-ms-bitfields (ver el documentation).

+0

¿Está documentado esto en algún lugar? Parece que no puedo encontrar nada útil en 'mno-ms-bitfields' –

+0

https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#x86-Options – ndkrempel

+0

Gracias, no estoy seguro de por qué podría no encontrar eso antes Sería útil editar eso en su respuesta ya que los comentarios no están destinados a ser a largo plazo. –

0

El uso de ambos GCC 4.7.1, este código rendimientos (de fabricación casera) y GCC 4.2.1 (LLVM/ruido metálico †) en Mac OS X 10.7.4 con una compilación de 64 bits 4 en -std=c99 modo:

#include <stdio.h> 

int main(void) 
{ 
    struct S 
    { 
     unsigned a : 4; 
     _Bool b : 1; 
    }; 
    printf("%zu\n", sizeof(struct S)); 
    return 0; 
} 

Esa es la mitad del tamaño que informa en Windows. Me parece sorprendentemente grande (esperaría que tenga un tamaño de 1 byte), pero las reglas de la plataforma son lo que son. Básicamente, el compilador no está obligado a seguir las reglas que le gustaría; puede seguir las reglas de la plataforma en la que se ejecuta, y donde tiene la oportunidad, incluso puede definir las reglas de la plataforma en la que se ejecuta.

Este programa siguiente tiene un comportamiento ligeramente dudosa (porque accede u.i después de u.s fue el último escrito a), pero muestra que el campo a se almacena en los 4 bits menos significativos y el campo b se almacena en el bit siguiente:

#include <stdio.h> 

int main(void) 
{ 
    union 
    { 
     struct S 
     { 
      unsigned a : 4; 
      _Bool b : 1; 
     } s; 
     int i; 
    } u; 
    u.i = 0; 
    u.s.a = 5; 
    u.s.b = 1; 
    printf("%zu\n", sizeof(struct S)); 
    printf("%zu\n", sizeof(u)); 
    printf("0x%08X\n", u.i); 
    u.s.a = 0xC; 
    u.s.b = 1; 
    printf("0x%08X\n", u.i); 
    return 0; 
} 

salida:

4 
4 
0x00000015 
0x0000001C 

† i686-manzana-darwin11-LLV m-gcc-4.2 (GCC) 4.2.1 (Basado en Apple Inc. compilación 5658) (LLVM build 2336.9.00)

Cuestiones relacionadas