Между некоторыми элементами есть несколько неиспользуемых байтов, чтобы сохранить правильное выравнивание . Например, указатель по умолчанию для эффективности располагается на 4-байтовых границах, то есть его адрес должен быть кратен 4. Если структура содержит только символ и указатель
struct {
char a;
void* b;
};
, то b
не может использовать сумматор №1 - он должен быть размещен в №4.
0 1 2 3 4 5 6 7
+---+- - - - - -+---------------+
| a | (unused) | b |
+---+- - - - - -+---------------+
В вашем случае дополнительные 7 байтов поступают из 3 байтов из-за выравнивания int *
и 4 байтов из-за выравнивания double
.
0 1 2 3 4 5 6 7 8 9 a b c d e f
+---------------+---+- - - - - -+---------------+- - - - - - - -+
| i |ch | | p | |
+---------------+---+- - - - - -+---------------+- - - - - - - -+
10 11 12 13 14 15 16 17
+-------------------------------+
| d |
+-------------------------------+
... это дает мне 24, и я сбит с толку. Каковы дополнительные 7 байтов?
Это байты заполнения, вставленные компилятором. Заполнение структуры данных зависит от реализации.
Из Википедии, Выравнивание структуры данных :
Выравнивание данных означает размещение данных со смещением памяти, равным некоторому кратному размеру слова, что увеличивает производительность системы из-за способа обработки ЦП объем памяти. Чтобы выровнять данные, может потребоваться вставить несколько бессмысленных байтов между концом последней структуры данных и началом следующей, что является заполнением структуры данных.
Чтобы немного расширить отличный ответ KennyDM (Кенни - пожалуйста, укради это, чтобы дополнить свой ответ, если хотите), это, вероятно, то, что вам запомнилось структура выглядит так, как только компилятор выровнял все переменные:
0 1 2 3 4 5 6 7
+-------------------+----+-----------+
| i | ch | (unused) |
+-------------------+----+-----------+
8 9 10 11 12 13 14 15
+-------------------+----------------+
| p | (unused) |
+-------------------+----------------+
16 17 18 19 20 21 22 23
+------------------------------------+
| d |
+------------------------------------+
Итак, из-за 3-байтового разрыва между «ch» и «p» и 4-байтового разрыва между «p» и «d», вы получаете 7-байтовый отступ для вашей структуры, таким образом, размер 24 байта. Поскольку ваша среда double
имеет 8-байтовое выравнивание (то есть он должен находиться в собственном 8-байтовом блоке, как вы можете видеть выше), вся структура
также будет 8- байт выровнен полностью, поэтому даже переупорядочение переменных не изменит размер с 24 байтов.
Компилятору разрешено выравнивать элементы структуры по адресам для более быстрого доступа. например 32-битные границы. Стандарт требует только, чтобы члены объекта хранились в том порядке, в котором они объявлены. Поэтому всегда убедитесь, что вы используете sizeof
и offsetof
, когда вам нужна точная позиция в памяти.
$ 9.2 / 12 заявляет - «Нестатические элементы данных класса (не-объединения), объявленные без промежуточного спецификатора доступа, выделяются так, чтобы более поздние члены имели более высокие адреса в объекте класса. Порядок распределения нестатических элементов данных, разделенных спецификатором доступа, не определено (11.1). Требования к согласованию реализации может привести к тому, что два соседних элемента не будут размещены сразу после друг друга; так может требования к пространству для управления виртуальными функциями (10.3) и виртуальными базовыми классами (10.1). "
Так же, как sizeof (double) и sizeof (int), смещения, по которым будут выровнены элементы структуры, не определены, за исключением того, что члены, объявленные позже, находятся по более высоким адресам.
Дополнительный размер возникает из-за выравнивания данных, т.е. члены выравниваются по кратным 4 или 8 байтам.
Ваш компилятор, вероятно, выравнивает int и указатели по кратным 4 байтам, а double - по кратным 8 байтам.
Если вы переместите double в другую позицию внутри struct, вы сможете уменьшить размер struct с 24 до 20 байт. Но это зависит от компилятора.
Также иногда вам может понадобиться структура для поддержания необходимого вам порядка. В этом случае, если вы используете gcc, вы должны использовать оператор __ attribute __ ((pack))
.
См. Также для получения дополнительной информации.
Это 24 байта из-за заполнения. Большинство компиляторов дополняют данные до кратного их размера. Итак, 4-байтовое int дополняется до числа, кратного 4 байтам. 8-байтовое двойное значение дополняется до числа, кратного 8 байтам. Для вашей структуры это означает:
struct struct_type{
int i; // offset 0 (0*4)
char ch; // offset 4 (4*1)
char padding1[3];
int *p; // offset 8 (2*4)
char padding1[4];
double d; // offset 16 (2*8)
}s;
Вы можете оптимизировать вашу структуру следующим образом:
struct struct_type{
double d;
int i;
int *p;
char ch;
}s;
sizeof (s) == 17 в большинстве компиляторов (20 в некоторых других)
См. список FAQ по comp.lang.c - Вопрос 2.12:
Почему мой компилятор оставляет дыры в структурах, теряя место и предотвращая ``бинарный'' ввод/вывод во внешние файлы данных? Могу ли я отключить это или иным образом контролировать выравнивание полей структуры?