Почему этот размер структуры 3 вместо 2?
Я определил эту структуру:
typedef struct
{
char A:3;
char B:3;
char C:3;
char D:3;
char E:3;
} col;
sizeof(col)
дает мне результат 3, но не должен ли он быть 2? Если я прокомментирую только один элемент, sizeof
равно 2. Я не понимаю, почему: пять элементов из 3 бит равны 15 битам и меньше 2 байтов.
Существует ли "внутренний размер" в определении структуры, подобной этой? Мне просто нужно уточнение, потому что из моего представления о языке до сих пор я ожидал размер 2 байта, а не 3.
Ответы
Ответ 1
Поскольку вы используете char
в качестве базового типа для своих полей, компилятор пытается группировать биты по байтам, и поскольку он не может помещать больше восьми бит в каждый байт, он может хранить только два поля на каждый байт.
Общая сумма бит, используемая вашей структурой, равна 15, поэтому идеальный размер для такого большого количества данных будет short
.
#include <stdio.h>
typedef struct
{
char A:3;
char B:3;
char C:3;
char D:3;
char E:3;
} col;
typedef struct {
short A:3;
short B:3;
short C:3;
short D:3;
short E:3;
} col2;
int main(){
printf("size of col: %lu\n", sizeof(col));
printf("size of col2: %lu\n", sizeof(col2));
}
Вышеприведенный код (для 64-битной платформы, такой как mine) действительно даст 2
для второй структуры. Для чего-либо большего, чем short
, структура заполняет не более одного элемента используемого типа, поэтому - для той же платформы - структура будет иметь размер четыре для int
, восемь для long
и т.д..
Ответ 2
Поскольку у вас не может быть бит поля пакета, который охватывает границу минимального выравнивания (которая составляет 1 байт), поэтому они, вероятно, будут упакованы как
byte 1
A : 3
B : 3
padding : 2
byte 2
C : 3
D : 3
padding : 2
byte 3
E : 3
padding : 5
(порядки поля/заполнения внутри одного и того же байта не являются преднамеренными, это просто для того, чтобы дать вам идею, поскольку компилятор мог бы отложить их, как это предпочитает)
Ответ 3
Первые два битовых поля вписываются в один char
. Третий не может вписаться в это char
и нуждается в новом. 3 + 3 + 3 = 9, который не вписывается в 8 бит char.
Итак, первая пара принимает char
, вторая пара принимает char
, а последнее поле бит получает третий char
.
Ответ 4
Большинство компиляторов позволяют вам управлять дополнением, например. используя #pragma
s. Вот пример с GCC 4.8.1:
#include <stdio.h>
typedef struct
{
char A:3;
char B:3;
char C:3;
char D:3;
char E:3;
} col;
#pragma pack(push, 1)
typedef struct {
char A:3;
char B:3;
char C:3;
char D:3;
char E:3;
} col2;
#pragma pack(pop)
int main(){
printf("size of col: %lu\n", sizeof(col)); // 3
printf("size of col2: %lu\n", sizeof(col2)); // 2
}
Обратите внимание, что поведение по умолчанию для компилятора по умолчанию существует и, вероятно, даст вам лучшую производительность.
Ответ 5
Несмотря на то, что в стандарте ANSI C указано слишком мало о том, как битовые поля упаковываются, чтобы предложить какое-либо существенное преимущество перед "компиляторам разрешено упаковывать битовые поля, но они сочтут нужным", тем не менее он во многих случаях запрещает компиляторам упаковывать вещи в наиболее эффективные мода.
В частности, если структура содержит битовые поля, компилятор должен хранить ее как структуру, которая содержит одно или несколько анонимных полей некоторого "нормального" типа хранилища, а затем логически подразделяет каждое такое поле на его составные части битового поля. Таким образом, учитывая:
unsigned char foo1: 3;
unsigned char foo2: 3;
unsigned char foo3: 3;
unsigned char foo4: 3;
unsigned char foo5: 3;
unsigned char foo6: 3;
unsigned char foo7: 3;
Если unsigned char
- 8 бит, компилятор должен будет выделить четыре поля этого типа и назначить два битовых поля всем, кроме одного (которое было бы в собственном поле char
). Если бы все объявления char
были заменены на short
, тогда было бы два поля типа short
, один из которых будет содержать пять битовых полей, а другой из них будет содержать оставшиеся два.
На процессоре без ограничений выравнивания данные могут быть более эффективны с помощью unsigned short
для первых пяти полей и unsigned char
для последних двух, сохраняя семь трехбитовых полей в трех байтах. В то время как должно быть возможно хранить восемь трехбитовых полей в трех байтах, компилятор мог разрешить это только в том случае, если существовал трехбайтовый числовой тип, который можно было использовать как тип "внешнего поля".
Лично я рассматриваю битовые поля как определено как бесполезные. Если код должен работать с двоично упакованными данными, он должен явно определять места хранения фактических типов, а затем использовать макросы или некоторые другие подобные средства для доступа к их битам. Было бы полезно, если бы C поддерживал такой синтаксис, как:
unsigned short f1;
unsigned char f2;
union foo1 = f1:0.3;
union foo2 = f1:3.3;
union foo3 = f1:6.3;
union foo4 = f1:9.3;
union foo5 = f1:12.3;
union foo6 = f2:0.3;
union foo7 = f2:3.3;
Такой синтаксис, если это разрешено, позволит коду использовать бит-поля переносимым образом, независимо от размеров слов или порядков байтов (foo0 будет в трех наименее значимых битах f1, но это может быть хранятся на более низком или более высоком адресе). Однако при отсутствии такой функции макросы, вероятно, являются единственным переносным способом работы с такими вещами.