为什么空数组的类型在结构内部和外部具有不同的大小?
Why has the type of an empty array a different size within and outside of a structure?
#include <stdio.h>
struct Obj {
char a;
uint32_t b;
uint8_t c;
uint64_t d[0];
};
struct Obj1 {
uint64_t d[0];
};
int main() {
uint64_t a[0];
printf("%d\n", sizeof(Obj)); // 16
printf("%d\n", sizeof(a)); // 16
printf("%d\n", sizeof(Obj1)); // 16
//cout << sizeof(Obj) << endl; // 16
//cout << sizeof(a) << endl; // 0
//cout << sizeof(Obj1) << endl; // 0
}
如上所示,为什么结构中的uint64_t
变量不会在uint8_t
之后立即堆叠,更奇怪的是空数组在结构之外的大小为零。
其实是一道面试题。解释是这样的,还是看不懂
If there is no fourth field, it should be 4+4+4=12, plus the fourth
field is 16, the fourth field does not occupy space, but it will tell
the compiler to align by 8 bytes
This usage is often used in the kernel, for example, the following can
be directly accessed by subscript
Obj o1; uint64_t array[1024]; // In memory, array immediately follows
o1 o1.d[123]; // can access the elements of array
如评论所述,这可能仅适用于 C 而不是 C++。所以我将代码更改为C版本。
首先,你的代码是未定义的行为。来自 个数组
p1 强调我的:
In a declaration T D where D has the form
D1 [ constant-expressionopt ] attribute-specifier-seqopt
and the type of the contained
declarator-id in the declaration T D1 is “derived-declarator-type-list
T”, the type of the declarator-id in D is
“derived-declarator-type-list array of N T”. The constant-expression
shall be a converted constant expression of type std::size_t
([expr.const]). Its value N specifies the array bound, i.e., the
number of elements in the array; N shall be greater than zero.
数组的大小必须大于 0。
至于 gcc 编译器扩展 在 C 代码中 allows for zero sized arrays 并且碰巧在 C++ 代码中也受支持,gcc 文档指出:
Although the size of a zero-length array is zero, an array member of this kind may increase the size of the enclosing type as a result of tail padding.
这似乎发生在您的代码中。
这个面试问题考察了候选人在 C 标准和特定实现中的对齐和某些语义的知识。
char a
成员的大小为一(字节),对齐要求为一(字节)。
uint32_t b
成员的大小为四,通常有四个字节的对齐要求。为了将其放置在四字节倍数的地址上,编译器必须在 a
之后和 b
之前包括三个未使用的字节,它们称为填充字节。至此,该结构需要1+3+4 = 8个字节。
uint8_t c
成员的大小为一,对齐要求为一。至此,该结构需要9个字节。
对于 uint64_t d[0]
,该行为未由 C 标准定义。但是,除非面试官指定这是一个关于严格遵守标准 C 的问题,否则回答行为未定义是不充分的,因为 C 不仅仅是标准。还有符合(但不严格符合)C 和 C 的 non-standard 变体。GCC 支持 well-known 扩展,其中结构的最后一个成员可以声明为具有零元素的数组,并且面试官希望提问者意识到这一点。
当使用这样的结构时,程序必须为它希望使用的任何数组元素分配足够的 space,方法是将这样的 space 添加到 malloc
或类似的 memory-allocation 例程。例如,要为基本结构加上 13 个元素分配 space,可以使用 malloc(sizeof(struct Obj) + 13 * sizeof(uint64_t))
.
通常,uint64_t
有八个字节的对齐要求。无论其对齐要求是什么,编译器都会在成员 c
和 d
之间添加足够的未使用字节,以确保 d
具有正确的对齐方式。如果确实需要eight-byte对齐,那么在c
之后必须插入7个字节,所以到d
开头的结构大小为1+3+4+1+7 = 16 个字节。
#include <stdio.h>
struct Obj {
char a;
uint32_t b;
uint8_t c;
uint64_t d[0];
};
struct Obj1 {
uint64_t d[0];
};
int main() {
uint64_t a[0];
printf("%d\n", sizeof(Obj)); // 16
printf("%d\n", sizeof(a)); // 16
printf("%d\n", sizeof(Obj1)); // 16
//cout << sizeof(Obj) << endl; // 16
//cout << sizeof(a) << endl; // 0
//cout << sizeof(Obj1) << endl; // 0
}
如上所示,为什么结构中的uint64_t
变量不会在uint8_t
之后立即堆叠,更奇怪的是空数组在结构之外的大小为零。
其实是一道面试题。解释是这样的,还是看不懂
If there is no fourth field, it should be 4+4+4=12, plus the fourth field is 16, the fourth field does not occupy space, but it will tell the compiler to align by 8 bytes
This usage is often used in the kernel, for example, the following can be directly accessed by subscript
Obj o1; uint64_t array[1024]; // In memory, array immediately follows
o1 o1.d[123]; // can access the elements of array
如评论所述,这可能仅适用于 C 而不是 C++。所以我将代码更改为C版本。
首先,你的代码是未定义的行为。来自 个数组 p1 强调我的:
In a declaration T D where D has the form
D1 [ constant-expressionopt ] attribute-specifier-seqopt
and the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list array of N T”. The constant-expression shall be a converted constant expression of type std::size_t ([expr.const]). Its value N specifies the array bound, i.e., the number of elements in the array; N shall be greater than zero.
数组的大小必须大于 0。
至于 gcc 编译器扩展 在 C 代码中 allows for zero sized arrays 并且碰巧在 C++ 代码中也受支持,gcc 文档指出:
Although the size of a zero-length array is zero, an array member of this kind may increase the size of the enclosing type as a result of tail padding.
这似乎发生在您的代码中。
这个面试问题考察了候选人在 C 标准和特定实现中的对齐和某些语义的知识。
char a
成员的大小为一(字节),对齐要求为一(字节)。
uint32_t b
成员的大小为四,通常有四个字节的对齐要求。为了将其放置在四字节倍数的地址上,编译器必须在 a
之后和 b
之前包括三个未使用的字节,它们称为填充字节。至此,该结构需要1+3+4 = 8个字节。
uint8_t c
成员的大小为一,对齐要求为一。至此,该结构需要9个字节。
对于 uint64_t d[0]
,该行为未由 C 标准定义。但是,除非面试官指定这是一个关于严格遵守标准 C 的问题,否则回答行为未定义是不充分的,因为 C 不仅仅是标准。还有符合(但不严格符合)C 和 C 的 non-standard 变体。GCC 支持 well-known 扩展,其中结构的最后一个成员可以声明为具有零元素的数组,并且面试官希望提问者意识到这一点。
当使用这样的结构时,程序必须为它希望使用的任何数组元素分配足够的 space,方法是将这样的 space 添加到 malloc
或类似的 memory-allocation 例程。例如,要为基本结构加上 13 个元素分配 space,可以使用 malloc(sizeof(struct Obj) + 13 * sizeof(uint64_t))
.
通常,uint64_t
有八个字节的对齐要求。无论其对齐要求是什么,编译器都会在成员 c
和 d
之间添加足够的未使用字节,以确保 d
具有正确的对齐方式。如果确实需要eight-byte对齐,那么在c
之后必须插入7个字节,所以到d
开头的结构大小为1+3+4+1+7 = 16 个字节。