2008-11-21 11 views
26

Acabo de hacer una prueba con bitfields, y los resultados me sorprenden.C++ bitfield packing con bools

class test1 { 
public: 
    bool test_a:1; 
    bool test_b:1; 
    bool test_c:1; 
    bool test_d:1; 
    bool test_e:1; 
    bool test_f:1; 
    bool test_g:1; 
    bool test_h:1; 
}; 

class test2 { 
public: 
    int test_a:1; 
    int test_b:1; 
    int test_c:1; 
    int test_d:1; 
    int test_e:1; 
    int test_f:1; 
    int test_g:1; 
    int test_h:1; 
}; 

class test3 { 
public: 
    int test_a:1; 
    bool test_b:1; 
    int test_c:1; 
    bool test_d:1; 
    int test_e:1; 
    bool test_f:1; 
    int test_g:1; 
    bool test_h:1; 
}; 

Los resultados fueron los siguientes: -

sizeof(test1) = 1 // This is what I'd expect. 8 bits in a byte 
sizeof(test2) = 4 // Reasonable. Maybe padded out to the size of an int. 
sizeof(test3) = 16 // What??? 

Es esto lo que cabría esperar, o un error del compilador? (Codegear C++ Builder 2007, por cierto ...)

+0

Si desea tener más control sobre el diseño de las estructuras de campo de bits en la memoria, considere usar este campo de bits, implementado como un archivo de encabezado de biblioteca: [link] (https://github.com/wkaras/C- plus-plus-library-bit-fields/blob/master/Bitfield.pdf) – WaltK

Respuesta

24

su compilador ha organizado todos los miembros de test3 en límites de tamaño entero. Una vez que se ha utilizado un bloque para un tipo determinado (campo de bits entero, o campo de bits booleano), el compilador no asigna ningún campo de bits adicional de un tipo diferente hasta el siguiente límite.

Dudo que sea un error. Probablemente tiene algo que ver con la arquitectura subyacente de su sistema.

edit:

C++ compiladores asignar campos de bits en la memoria de la siguiente manera: varios miembros campo de bits consecutivos del mismo tipo serán asignados secuencialmente. Tan pronto como sea necesario asignar un nuevo tipo, se alineará con el comienzo del siguiente bloque de memoria lógica. El siguiente bloque lógico dependerá de tu procesador. Algunos procesadores pueden alinearse con límites de 8 bits, mientras que otros solo pueden alinearse con límites de 16 bits.

En su test3, cada miembro es de un tipo diferente al anterior, por lo que la asignación de memoria será 8 * (el tamaño mínimo de bloque lógico en su sistema). En su caso, el tamaño de bloque mínimo es de dos bytes (16 bits), por lo que el tamaño de test3 es 8 * 2 = 16.

En un sistema que puede asignar bloques de 8 bits, esperaría que el tamaño ser 8.

+1

Pero, si ese es el caso, por qué 16, en lugar de 20 ((4 + 1) * 4) o 32 ((4 + 4) * 4)? –

+0

Supongo que su sistema no puede alinearse con nada más pequeño que los límites de 16 bits. Cuando se asigna test_a: 1, ocupa el primer bit de un campo de 16 bits. Cuando se asigna test_b: 1, es de un tipo diferente, por lo que el compilador lo inicia en el siguiente límite de 16 bits, para un total de 128 bits. –

1

No es lo que esperaba, ya que el orden es significativo. Si agruparas los bools y los ints obtendrías un resultado muy diferente. Como recuerdo, los bitfields solo funcionan en un tipo común.

7

Guau, eso es sorprendente. En GCC 4.2.4, los resultados son 1, 4 y 4, respectivamente, ambos en los modos C y C++. Este es el programa de prueba que utilicé y que funciona tanto en C99 como en C++.

#ifndef __cplusplus 
#include <stdbool.h> 
#endif 
#include <stdio.h> 

struct test1 { 
    bool test_a:1; 
    bool test_b:1; 
    bool test_c:1; 
    bool test_d:1; 
    bool test_e:1; 
    bool test_f:1; 
    bool test_g:1; 
    bool test_h:1; 
}; 

struct test2 { 
    int test_a:1; 
    int test_b:1; 
    int test_c:1; 
    int test_d:1; 
    int test_e:1; 
    int test_f:1; 
    int test_g:1; 
    int test_h:1; 
}; 

struct test3 { 
    int test_a:1; 
    bool test_b:1; 
    int test_c:1; 
    bool test_d:1; 
    int test_e:1; 
    bool test_f:1; 
    int test_g:1; 
    bool test_h:1; 
}; 

int 
main() 
{ 
    printf("%zu %zu %zu\n", sizeof (struct test1), sizeof (struct test2), 
          sizeof (struct test3)); 
    return 0; 
} 
+0

intente poner struct test3 {public: ....; }; dado que antes hay un modificador de acceso, el compilador ya no puede volver a ordenar. (hay una oración en la norma que indica que después de tal cosa, hasta que la otra, la reordenación no está permitida) –

+0

hm. no, acabo de probarlo. eso no lo hizo más grande:/ –

4

Como observación general, una firmada int de 1 bit no tiene mucho sentido. Claro, probablemente puedas averiguar cómo almacenar 0 en él, pero luego comienza el problema.

Un bit debe ser el bit de signo, incluso en complemento a dos, pero solo tiene un bit para jugar. Entonces, si asigna eso como bit de signo, no le quedan bits para el valor real. Es cierto como Steve Jessop señala en un comentario que probablemente podrías representar -1 si se usa el complemento de dos, pero aún creo que un tipo de datos "entero" que solo puede representar 0 y -1 es algo bastante extraño.

Para mí, este tipo de datos no hace (o, teniendo en cuenta el comentario de Steve, little) sense.

Utilice unsigned int small : 1; para que no tenga signo, luego puede almacenar los valores 0 y 1 de una manera no ambigua.

+0

jeje. Si es el complemento de uno, solo puede almacenar más y menos cero ... Lo había usado como ejemplo. Mi código 'real' cuando pulsé esto fue usando uints. – Roddy

+10

Si se trata de un valor de 1 bit firmado con un complemento de dos, entonces un bit claro representa 0 y un bit establecido representa -1. ¿Dónde está el problema? ;-) –

16

que tener cuidado con campos de bits como gran parte de su comportamiento consiste en la realización (compilador) definida:

De C++ 03, 9.6 Campos de bits (Pág. 163):

Asignación de campos de bits dentro de un objeto de clase es definido por la implementación. La alineación de bit-fields está definida por la implementación. Los campos de bits se empaquetan en alguna unidad de asignación direccionable . [Nota: Asignación de estratos de bit de campos unidades en algunas máquinas y no en otras. Los campos de bits tienen asignada de derecha a izquierda en algunas máquinas, de izquierda a derecha en otras. ]

Es decir, no es un error en el compilador sino una falta de una definición estándar de cómo debe comportarse.

1
#include <iostream> 
using namespace std; 

bool ary_bool4[10]; 

struct MyStruct { 
    bool a1 :1; 
    bool a2 :1; 
    bool a3 :1; 
    bool a4 :1; 
    char b1 :2; 
    char b2 :2; 
    char b3 :2; 
    char b4 :6; 
    char c1; 
}; 

int main() { 
    cout << "char size:\t" << sizeof(char) << endl; 
    cout << "short int size:\t" << sizeof(short int) << endl; 
    cout << "default int size:\t" << sizeof(int) << endl; 
    cout << "long int size:\t" << sizeof(long int) << endl; 
    cout << "long long int size:\t" << sizeof(long long int) << endl; 
    cout << "ary_bool4 size:\t" << sizeof(ary_bool4) << endl; 
    cout << "MyStruct size:\t" << sizeof(MyStruct) << endl; 
    // cout << "long long long int size:\t" << sizeof(long long long int) << endl; 
    return 0; 
} 

char size: 1 
short int size: 2 
default int size: 4 
long int size: 4 
long long int size: 8 
ary_bool4 size: 10 
MyStruct size: 3 
0

De "Samuel P. Harbison, Guy L. Steele] CA Referencia":

El problema:

"Los compiladores son libres de imponer limitaciones al tamaño máximo de un bit campo, y especifique ciertos límites de direccionamiento que ese campo de bit no puede cruzar ".

manipulaciones que se pueden hacer dentro del estándar:

"Un campo de bits sin nombre también puede ser incluido en una estructura para proporcionar el acolchado".

"Especificar una longitud de 0 para el campo de bit sin nombre tiene un significado especial - indica que no se deben empaquetar más campos de bits en el área en la que el campo de bit anterior ... Área significa alguna unidad de almacenamiento definida implícita "

¿Es esto lo que esperaría, o un error del compilador?

Así que dentro de C89, C89 con la enmienda I, C99 - no es un error. Acerca de C++ No lo sé, pero creo que el comportamiento es similar.