在具有灵活数组的结构的初始化中使用 sizeof 运算符
Using the sizeof operator in the initialization of a struct with a flexible array
我想声明一个包含灵活数组成员的结构,然后在其上使用 sizeof()
。原型为:
typedef struct
{
uint16_t length;
uint8_t array[][2];
} FLEXIBLE_t;
然后我声明它:
const FLEXIBLE_t test = {
.length = sizeof(test),
.array = { { 0, 1 },
{ 2, 3 },
{ 4, 5 },
{ 6, 7 },
{ 8, 9 } }
};
一切都编译正常(GCC)但是当我检查 test.length
它的值为 2,即它只计算 length
本身的 uint16_t
。
如何在编译时计算结构体的大小?看来编译器使用的是原型而不是具体实例。
sizeof
忽略灵活数组成员,因为灵活数组成员在结构中不使用 space。
C11-§6.7.2.2/18
As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply. [...]
请注意,标准 C 不允许在您的代码中进行灵活的数组成员初始化。它将调用未定义的行为(参见§6.7.2.2 第 20 和 21 段)。虽然 GCC 允许将其作为 extension:
GCC allows static initialization of flexible array members. This is equivalent to defining a new structure containing the original structure followed by an array of sufficient size to contain the data.
从 sizeof()
运算符返回的大小(几乎)忽略了灵活数组。
根据 C Standard, 6.7.2.1 Structure and union specifiers, paragraph 18:
As a special case, the last element of a structure with more than one
named member may have an incomplete array type; this is called a
flexible array member . In most situations, the flexible array
member is ignored. In particular, the size of the structure
is as if the flexible array member were omitted except that
it may have more trailing padding than the omission would imply.
灵活的数组成员不计入大小:
... In particular, the size of the structure is as if the flexible
array member were omitted except that it may have more trailing
padding than the omission would imply.
除了大小问题之外,您的代码在 C 中还有未定义的行为。灵活的数组成员不能像那样初始化。 Gcc 在这个意义上可能有一个扩展,但它不可移植。
GCC 允许初始化灵活数组作为扩展:https://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Zero-Length.html
但是,sizeof() 遵循 C 标准并认为结构中的灵活数组的大小为零。在任何情况下,当尝试在结构的初始值设定项中使用 sizeof() 时,该结构在那个阶段是不完整的,并且最终大小尚不清楚。只有原型的大小及其 zero-length 灵活数组是已知的。
然而,大小在编译时是已知的,只是在结构初始化之后才知道。在这种情况下,GCC 的 __builtin_object_size()
将评估为数字常量,但必须从函数调用,因为它并不总是常量,因此不能在初始化程序中使用。
所以 .length
必须在 运行 时赋值,但至少被赋值的值编译成常量:
test.length = __builtin_object_size(test, 0);
我想声明一个包含灵活数组成员的结构,然后在其上使用 sizeof()
。原型为:
typedef struct
{
uint16_t length;
uint8_t array[][2];
} FLEXIBLE_t;
然后我声明它:
const FLEXIBLE_t test = {
.length = sizeof(test),
.array = { { 0, 1 },
{ 2, 3 },
{ 4, 5 },
{ 6, 7 },
{ 8, 9 } }
};
一切都编译正常(GCC)但是当我检查 test.length
它的值为 2,即它只计算 length
本身的 uint16_t
。
如何在编译时计算结构体的大小?看来编译器使用的是原型而不是具体实例。
sizeof
忽略灵活数组成员,因为灵活数组成员在结构中不使用 space。
C11-§6.7.2.2/18
As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply. [...]
请注意,标准 C 不允许在您的代码中进行灵活的数组成员初始化。它将调用未定义的行为(参见§6.7.2.2 第 20 和 21 段)。虽然 GCC 允许将其作为 extension:
GCC allows static initialization of flexible array members. This is equivalent to defining a new structure containing the original structure followed by an array of sufficient size to contain the data.
从 sizeof()
运算符返回的大小(几乎)忽略了灵活数组。
根据 C Standard, 6.7.2.1 Structure and union specifiers, paragraph 18:
As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member . In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.
灵活的数组成员不计入大小:
... In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.
除了大小问题之外,您的代码在 C 中还有未定义的行为。灵活的数组成员不能像那样初始化。 Gcc 在这个意义上可能有一个扩展,但它不可移植。
GCC 允许初始化灵活数组作为扩展:https://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Zero-Length.html
但是,sizeof() 遵循 C 标准并认为结构中的灵活数组的大小为零。在任何情况下,当尝试在结构的初始值设定项中使用 sizeof() 时,该结构在那个阶段是不完整的,并且最终大小尚不清楚。只有原型的大小及其 zero-length 灵活数组是已知的。
然而,大小在编译时是已知的,只是在结构初始化之后才知道。在这种情况下,GCC 的 __builtin_object_size()
将评估为数字常量,但必须从函数调用,因为它并不总是常量,因此不能在初始化程序中使用。
所以 .length
必须在 运行 时赋值,但至少被赋值的值编译成常量:
test.length = __builtin_object_size(test, 0);