在Linux内核代码中,我发现了以下无法理解的内容.
struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
Run Code Online (Sandbox Code Playgroud)
代码在这里:http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h
零元素数据数组的需求和目的是什么?
Sha*_*baz 133
这是一种可变数据大小的方法,无需调用malloc(kmalloc在本例中)两次.你会像这样使用它:
struct bts_action *var = kmalloc(sizeof(*var) + extra, GFP_KERNEL);
Run Code Online (Sandbox Code Playgroud)
这曾经不是标准的,被认为是黑客(如Aniket所说),但它在C99中被标准化.它现在的标准格式是:
struct bts_action {
u16 type;
u16 size;
u8 data[];
} __attribute__ ((packed)); /* Note: the __attribute__ is irrelevant here */
Run Code Online (Sandbox Code Playgroud)
请注意,您没有提到该data字段的任何大小.另请注意,此特殊变量只能位于结构的末尾.
在C99中,这个问题在6.7.2.1.16中解释(强调我的):
作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型; 这被称为灵活的阵列成员.在大多数情况下,将忽略灵活数组成员.特别地,结构的尺寸好像省略了柔性阵列构件,除了它可以具有比省略意味着更多的拖尾填充.但是,当一个.(或 - >)运算符有一个左操作数,它是一个带有灵活数组成员的结构(一个指针),右操作数命名该成员,它的行为就好像该成员被最长的数组替换(具有相同的元素类型) )不会使结构大于被访问的对象; 数组的偏移量应保持为灵活数组成员的偏移量,即使这与替换数组的偏移量不同.如果这个数组就没有的元素,它的行为就好像它有一个元素,但如果任何试图访问该元素或产生一个指向一个过去,它的行为是不确定的.
或者换句话说,如果你有:
struct something
{
/* other variables */
char data[];
}
struct something *var = malloc(sizeof(*var) + extra);
Run Code Online (Sandbox Code Playgroud)
您可以使用var->data索引访问[0, extra).请注意,sizeof(struct something)只会给出其他变量data的大小,即给出大小为0.
值得注意的是,标准实际上给出了malloc这样一个结构的例子(6.7.2.1.17):
struct s { int n; double d[]; };
int m = /* some value */;
struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));
Run Code Online (Sandbox Code Playgroud)
该标准在同一地点的另一个有趣的注释是(强调我的):
假设对malloc的调用成功,p指向的对象在大多数情况下表现得像p被声明为:
Run Code Online (Sandbox Code Playgroud)struct { int n; double d[m]; } *p;(在某些情况下,这种等价性被破坏;特别是,成员d的偏移量可能不同).
Ani*_*nge 36
它也被称为结构黑客.
所以下一次,我会说:
struct bts_action *bts = malloc(sizeof(struct bts_action) + sizeof(char)*100);
Run Code Online (Sandbox Code Playgroud)
这相当于说:
struct bts_action{
u16 type;
u16 size;
u8 data[100];
};
Run Code Online (Sandbox Code Playgroud)
我可以创建任意数量的此类结构对象.
我们的想法是在结构的末尾允许一个可变大小的数组.据推测,bts_action是一些具有固定大小标头(type和size字段)的数据包,以及可变大小的data成员.通过将其声明为0长度数组,可以将其编入索引,就像任何其他数组一样.然后你会分配一个bts_action结构,比如1024字节data大小,如下所示:
size_t size = 1024;
struct bts_action* action = (struct bts_action*)malloc(sizeof(struct bts_action) + size);
Run Code Online (Sandbox Code Playgroud)
另见:http://c2.com/cgi/wiki ?StructHack
代码无效C(见此).出于显而易见的原因,Linux内核没有丝毫关注可移植性,因此它使用了大量非标准代码.
他们正在做的是一个数组大小为0的GCC非标准扩展.一个符合标准的程序就已经编写了u8 data[];,它意味着完全相同的东西.Linux内核的作者显然喜欢让事情变得不必要地复杂和非标准,如果这样做的选择显露出来的话.
在较旧的C标准中,以空数组结束结构被称为"结构黑客".其他人已经在其他答案中解释了其目的.在C90标准中,struct hack是未定义的行为并且可能导致崩溃,主要是因为C编译器可以在结构的末尾自由添加任意数量的填充字节.这样的填充字节可能会与您尝试在结构末端"入侵"的数据发生冲突.
GCC早期做了一个非标准的扩展,将其从未定义的行为转变为定义明确的行为.然后,C99标准采用了这一概念,因此任何现代C程序都可以无风险地使用此功能.它被称为C99/C11中的柔性阵列构件.