2008-11-21 3 views
26

Я только что прошел тест с битполями, и результаты меня удивляют.C++ bitfield упаковка с 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; 
}; 

Результаты были: -

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??? 

Это то, что вы ожидаете, или ошибка компилятора? (Codegear C++ Builder 2007, btw ...)

+0

Если вы хотите получить больше контроля над компоновкой структур битового поля в памяти, рассмотрите возможность использования этого объекта бит-поля, реализованного в виде файла заголовка библиотеки: [link] (https://github.com/wkaras/C- plus-plus-library-bit-fields/blob/master/Bitfield.pdf) – WaltK 2017-01-04 18:27:37

ответ

24

ваш компилятор упорядочил все члены test3 на границах целочисленного размера. Как только блок используется для данного типа (целочисленное битовое поле или логическое битовое поле), компилятор не выделяет никаких дополнительных полей бит другого типа до следующей границы.

Я сомневаюсь, что это ошибка. Вероятно, это связано с базовой архитектурой вашей системы.

редактировать:

C++ компиляторы будет выделять битовые поля в памяти следующим образом: несколько последовательных членов битового поля одного и того же типа будут распределены последовательно. Как только новый тип должен быть выделен, он будет выровнен с началом следующего блока логической памяти. Следующий логический блок будет зависеть от вашего процессора. Некоторые процессоры могут согласовываться с 8-битными границами, в то время как другие могут согласовывать только с 16-разрядными границами.

В вашем тесте3 каждый элемент имеет другой тип, чем тот, который был перед ним, поэтому распределение памяти будет 8 * (минимальный размер логического блока в вашей системе). В вашем случае минимальный размер блока составляет два байта (16 бит), поэтому размер test3 равен 8 * 2 = 16.

В системе, которая может выделять 8-битные блоки, я ожидал бы, что размер будет равен be 8.

+1

Но если это так, почему 16 вместо 20 ((4 + 1) * 4) или 32 ((4 + 4) * 4)? – 2008-11-21 10:45:12

+0

Я предполагаю, что ваша система не может выровняться с чем-то меньшим, чем 16-битные границы. Когда test_a: 1 выделяется, он занимает первый бит 16-битного поля. Когда назначается test_b: 1, он имеет другой тип, поэтому компилятор запускает его на следующей 16-разрядной границе, в общей сложности 128 бит. – 2008-11-21 10:58:16

1

Нет, это то, чего я ожидал бы, поскольку порядок значителен. Если вы сгруппировали bools и ints, вы получите совсем другой результат. Как я помню, битполы работают только по общему типу.

7

Вау, это удивительно. В GCC 4.2.4 результаты 1, 4 и 4 соответственно, как в режимах C, так и в C++. Вот тестовая программа, которую я использовал, которая работает как на C99, так и на 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

попробуйте поставить struct test3 {public: ....; }; поскольку перед ним есть модификатор доступа, компилятору больше не разрешается переупорядочивать. (в стандарте указано предложение, что после такой вещи, до другой, переупорядочение не допускается) – 2008-11-21 12:45:42

4

В качестве общего наблюдения, подписанная int из 1 бит не делает много смысла. Конечно, вы, вероятно, можете выяснить, как хранить в нем 0, но затем начинается проблема.

Один бит должен быть знаковым битом, даже в дополнении, но у вас есть только один бит для игры. Таким образом, если вы выделите это как знаковый бит, у вас нет битов, оставшихся для фактического значения. Это правда, поскольку Стив Джессоп указывает в комментарии, что вы, вероятно, можете представить -1, если используете два дополнения, но я все же считаю, что «целочисленный» тип данных, который может представлять только 0 и -1, является довольно странным.

Для меня этот тип данных не делает (или, учитывая комментарий Стива, немного) смысл.

Используйте unsigned int small : 1;, чтобы сделать его неподписанным, тогда вы можете хранить значения 0 и 1 недвусмысленным образом.

16

Будьте осторожны с битовых полей, как большая часть его поведения является реализация (составитель) определяется:

От C++ 03, 9,6 Битовые поля (стр. 163):

Распределение битовых полей в Объект класса - это . Выравнивание битовых полей определяется реализацией. Бит-поля упакованы в адресный блок выделения . [Примечание: распределение полей бит-полей единиц на некоторых машинах, а не на другие. Бит-поля назначаются справа налево на некоторых машинах, слева направо на других. ]

То есть, это не ошибка в компиляторе, а отсутствие стандартного определения того, как оно должно себя вести.

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

От "Самюэль П. Харбисона, Л. Стил Гай] CA Reference":

Проблема:

«Составители свободно накладывают ограничения на максимальный размер бита поле и указать определенные границы адресации, которые бит-бит не может пересечь ».

манипуляциях, которые могут быть сделаны в пределах стандарта:

«Неназванный битовое поле, также могут быть включены в структуру, чтобы обеспечить заполнение.»

«Указать длину 0 для безымянного битового поля имеет особое значение - это указывает на то, что больше не должно быть упаковано больше полей бит в область, в которой предыдущее поле бит ... Область здесь означает некоторый определенный им блок памяти "

Это то, что вы ожидаете, или ошибка компилятора?

Итак, в рамках C89, C89 с поправкой I, C99 - это не ошибка. О C++ я не знаю, но я думаю, что поведение похоже.

Смежные вопросы