Я часто нахожу термин «заполнение битами» в отношении типов данных, но не понимаю, что это такое, и что он делает с ними.
Суть этогоэто они "впустую" пространство. Я говорю «впустую», потому что, хотя биты-отступы делают объект больше, он может значительно облегчить работу с ним (что означает более быструю), а небольшие потери пространства могут привести к значительному увеличению производительности. В некоторых случаях это важно, потому что ЦП не может работать с объектами такого размера.
Допустим, у вас есть структура наподобие (все числа являются лишь примером, разные платформы могут иметь разные значения):
struct foo
{
short a; // 16 bits
char b; // 8 bits
};
и машина, с которой вы работаете, читает 32 бита данных за одну операцию чтения. Чтение одного foo не является проблемой, поскольку весь объект помещается в этот 32-битный блок. Что становится проблемой, когда у вас есть массив. Важно помнить о массивах, потому что они смежные, между элементами нет места. Это просто один объект, за которым сразу следует другой. Итак, если у вас есть массив, подобный
foo array[10]{};
С этим первый foo
объект находится в 32-битном сегменте. Следующий элемент массива будет находиться в первом 32-разрядном сегменте и во втором 32-разрядном сегменте. Это означает, что элемент a
находится в двух отдельных сегментах. Некоторые процессоры могут сделать это (за плату), а другие процессоры просто рухнут, если вы попытаетесь это сделать. Чтобы решить обе эти проблемы, компилятор добавит биты заполнения в конец foo
, чтобы заполнить его размер. Это означает, что foo фактически становится
struct foo
{
short a; // 16 bits
char b; // 8 bits
char _; // 8 bits of padding
};
И теперь процессору легко обрабатывать foo
объекты самостоятельно или в массиве. Это не требует дополнительной работы, и вы добавили только 8 бит на объект. Вам понадобится много объектов, чтобы это начало иметь значение на современном компьютере.
В некоторых случаях вам также необходимо заполнение между членами типа из-за невыровненного доступа. Допустим, у вас есть
struct bar
{
char c; // 8 bits
int d; // 32 bits
};
Теперь bar
имеет ширину 40 бит и d
чаще, чем не, будет снова сохраняться в двух разных. Чтобы исправить это, компилятор добавляет биты заполнения между c
и d
, такими как
struct bar
{
char c; // 8 bits
char _[3]; // 24 bits
int d; // 32 bits
};
, и теперь d
гарантированно входит в одно 32-битное ведро.