я не понимаю почему
struct e{
void * a;
void * b[];
}
имеет sizeof(e) == 4, а
struct f{
void * a;
void * b;
}
имеет sizeof(f) == 8.
я не понимаю почему
struct e{
void * a;
void * b[];
}
имеет sizeof(e) == 4, а
struct f{
void * a;
void * b;
}
имеет sizeof(f) == 8.
Второй в первой структуре — это не указатель, а FAM — гибкий член массива. Он используется, когда у вас есть длинный буфер и вы помещаете e
в начало этого буфера. Затем вы можете проиндексировать оставшуюся память, которая следует за объектом e
, используя этот FAM, и рассматривать эту память как массив void*
.
Стандарт говорит (выделено мной)
В особом случае последний элемент структуры с более чем одним именованным членом может иметь тип неполного массива; это называется гибким элементом массива. В большинстве случаев гибкий элемент массива игнорируется. В частности, размер структуры такой же, как если бы член гибкого массива был опущен, за исключением того, что он может иметь большее количество завершающих отступов, чем предполагает пропуск.
Например, следующий код выводит 1
для структуры без, но 4
для структуры с FAM на GCC, потому что для доступа к целым числам FAM необходимо правильно выровнять (в этом примере на границе 4 байта)
struct A {
char a;
};
struct B {
char a;
int flex[];
};
int main() {
printf("sizeof A: %d\nsizeof B: %d\n",
(int)sizeof(struct A),
(int)sizeof(struct B)
);
struct B *b = malloc(sizeof *b + sizeof(int[3]));
b->a = 'X';
b->flex[0] = 1;
b->flex[1] = 2;
b->flex[2] = 3;
free(b);
}
struct e{
void * a;
void * b[];
// ^^
}
[]
в структуре делает b
гибким членом массива C99. Таким образом, sizeof(e)
будет учитывать только размер a
, который равен 4.
Из C99 §6.7.2.1/16:
В особом случае последний элемент структуры с более чем одним именованным членом может иметь тип неполного массива; это называется гибкий элемент массива. В большинстве случаев гибкий элемент массива игнорируется. В частности, размер структуры такой же, как если бы элемент гибкого массива был опущен, за исключением того, что он может иметь большее количество завершающих отступов, чем предполагает пропуск.
Однако, когда оператор
.
(или->
) имеет левый операнд, который является (указателем) структурой с гибким элементом массива, а правый операнд называет этот элемент, он ведет себя так, как если бы этот элемент был заменен самым длинным массивом (с тот же тип элемента), который не делал бы структуру больше, чем объект, к которому осуществляется доступ; смещение массива должно оставаться смещением гибкого элемента массива, даже если оно будет отличаться от смещения замещающего массива. Если этот массив не имеет элементов, он ведет себя так, как если бы он имел один элемент, но поведение не определено, если предпринимается какая-либо попытка получить доступ к этому элементу или сгенерировать указатель после него.
b
. 16.09.2010 sizeof
структуры. Если вы поместите его в стек, доступ к нему будет UB. 16.09.2010 struct FOO {int32 x,y;}; struct BAR {int32 a,b; FOO dat[];}, and one allocates a
BAR *boz` с 36 байтами, можно ли легально получить доступ к boz->dat[3].x
? Хотя boz->dat[3]
не помещается полностью в выделенное пространство, boz->dat[3].x
должно начинаться через 32 байта после начала boz
и, таким образом, должно помещаться в выделенное пространство. 06.10.2012 Это связано с тем, что вторая структура использует гибкий массив элементов. Объяснение размера результата находится в Википедии.
void * b[];
недействителен в C89
, поэтому это означает, что вы используете компилятор C99
.
C99
ввел средство для определения «структурного хака»: теперь он называется «гибкий элемент массива», и до того, как ему будет выделена память, его размер равен 0.
Потому что первый не объявляет места для указателей в «b».