struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
代码在这里:http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h 一个零元素的数据数组有什么需要和目的?
struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
这是一种可以使用变量大小的数据,而不必调用malloc
(在这种情况下是kmalloc
)两次的方法。您可以像这样使用它:
struct bts_action *var = kmalloc(sizeof(*var) + extra, GFP_KERNEL);
这曾经不是标准的做法,被视为一种hack(正如Aniket所说),但它在C99中已经得到了标准化。现在它的标准格式是:
这曾经不是标准的做法,被视为一种hack(正如Aniket所说),但它在C99中已经得到了标准化。现在它的标准格式是:
struct bts_action {
u16 type;
u16 size;
u8 data[];
} __attribute__ ((packed)); /* Note: the __attribute__ is irrelevant here */
注意,你没有为data
字段指定大小。同时,这个特殊的变量只能出现在结构体的末尾。
在C99中,这个问题在6.7.2.1.16中有所说明(重点在我身上):
作为特例,一个拥有多个命名成员的结构体的最后一个元素可能具有不完整的数组类型;这被称为灵活数组成员。在大多数情况下,灵活数组成员将被忽略。特别地,在结构体的大小方面,它的大小就像灵活数组成员被省略一样,除了它可能有比省略要求更多的填充。但是,当一个点(或->)运算符具有左操作数(指向)一个具有灵活数组成员的结构体,并且右操作数指定该成员时,它的行为就好像该成员被替换为最长的数组(具有相同元素类型),该数组不会使对象大于被访问的对象;即使这将导致偏移量与替换数组的偏移量不同,数组的偏移量仍然应保持与灵活数组成员的偏移量相同。如果此数组没有任何元素,它的行为就好像它有一个元素,但如果尝试访问该元素或生成超出它的指针,则行为是未定义的。
换句话说,如果你有:
struct something
{
/* other variables */
char data[];
}
struct something *var = malloc(sizeof(*var) + extra);
你可以使用索引在 [0,extra)
范围内访问 var->data
。请注意,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]));
标准文档同一地点的另一个有趣的注释是(重点在于我):
假设调用 malloc 成功,指针 p 指向的对象在大多数情况下表现得好像 p 被声明为:
struct { int n; double d[m]; } *p;
在某些情况下,这种等价可能会被打破; 特别是,成员d的偏移量可能不相同。
[0, extra)
? - S.S. Annestruct bts_action *bts = malloc(sizeof(struct bts_action) + sizeof(char)*100);
它等价于说:
struct bts_action{
u16 type;
u16 size;
u8 data[100];
};
我可以创建任意数量的这样的结构对象。
这个想法是允许在结构体末尾使用可变大小的数组。可以假设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);
See also: http://c2.com/cgi/wiki?StructHack
malloc
的风格让你多次重复自己,如果 action
的类型发生更改,你就必须多次修复它。自行比较以下两者,你就会知道:struct some_thing *variable = (struct some_thing *)malloc(10 * sizeof(struct some_thing));
与 struct some_thing *variable = malloc(10 * sizeof(*variable));
,后者更短、更干净,而且更容易修改。 - Shahbazu8 data[];
,这意味着完全相同的事情。Linux内核的作者显然喜欢让事情变得更加复杂和非标准,如果有这样的选项出现的话。零长度数组的另一个用途是作为结构体内的命名标签,以协助编译时检查结构体偏移量。
假设您有一些大型结构定义(跨越多个缓存行),您希望确保它们在开始和中间跨越边界时都对齐到缓存行边界。
struct example_large_s
{
u32 first; // align to CL
u32 data;
....
u64 *second; // align to second CL after the first one
....
};
在代码中,您可以使用GCC扩展声明它们,例如:
__attribute__((aligned(CACHE_LINE_BYTES)))
但是你仍然希望确保在运行时执行此操作。
ASSERT (offsetof (example_large_s, first) == 0);
ASSERT (offsetof (example_large_s, second) == CACHE_LINE_BYTES);
assert (offsetof (one_struct, <name_of_first_member>) == 0);
assert (offsetof (one_struct, <name_of_second_member>) == CACHE_LINE_BYTES);
assert (offsetof (another_struct, <name_of_first_member>) == 0);
assert (offsetof (another_struct, <name_of_second_member>) == CACHE_LINE_BYTES);
与其这样做,您可以在结构体中声明一个零长度的数组,作为带有一致名称但不占用任何空间的命名标签。
#define CACHE_LINE_ALIGN_MARK(mark) u8 mark[0] __attribute__((aligned(CACHE_LINE_BYTES)))
struct example_large_s
{
CACHE_LINE_ALIGN_MARK (cacheline0);
u32 first; // align to CL
u32 data;
....
CACHE_LINE_ALIGN_MARK (cacheline1);
u64 *second; // align to second CL after the first one
....
};
assert (offsetof (one_struct, cacheline0) == 0);
assert (offsetof (one_struct, cacheline1) == CACHE_LINE_BYTES);
assert (offsetof (another_struct, cacheline0) == 0);
assert (offsetof (another_struct, cacheline1) == CACHE_LINE_BYTES);