Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 什么';需要零元素数组吗?_C_Structure_Flexible Array Member - Fatal编程技术网

C 什么';需要零元素数组吗?

C 什么';需要零元素数组吗?,c,structure,flexible-array-member,C,Structure,Flexible Array Member,在Linux内核代码中,我发现了以下我无法理解的东西 struct bts_action { u16 type; u16 size; u8 data[0]; } __attribute__ ((packed)); 代码如下: 零元素数据数组的需求和用途是什么?这实际上是一个黑客行为,实际上是针对()的 它也叫a 所以下一次,我会说: struct bts_action *bts = malloc(sizeof(struct bts_ac

在Linux内核代码中,我发现了以下我无法理解的东西

 struct bts_action {
         u16 type;
         u16 size;
         u8 data[0];
 } __attribute__ ((packed));
代码如下:


零元素数据数组的需求和用途是什么?

这实际上是一个黑客行为,实际上是针对()的

它也叫a

所以下一次,我会说:

struct bts_action *bts = malloc(sizeof(struct bts_action) + sizeof(char)*100);
这相当于说:

struct bts_action{
    u16 type;
    u16 size;
    u8 data[100];
};

我可以创建任意数量的这样的结构对象。

思想是在结构的末尾允许一个大小可变的数组。大概,
bts_action
是一个具有固定大小报头(
类型
大小
字段)和可变大小
数据
成员的数据包。通过将其声明为0长度数组,它可以像任何其他数组一样进行索引。然后分配一个
bts\u动作
struct,比如1024字节
数据
大小,如下所示:

size_t size = 1024;
struct bts_action* action = (struct bts_action*)malloc(sizeof(struct bts_action) + size);

另请参见:

这是一种数据大小可变的方法,无需调用
malloc
kmalloc
)两次。您可以这样使用它:

struct bts_action *var = kmalloc(sizeof(*var) + extra, GFP_KERNEL);
这曾经不是标准的,被认为是一种黑客行为(正如Aniket所说),但它在C99中被标准化了。现在的标准格式是:

struct bts_action {
     u16 type;
     u16 size;
     u8 data[];
} __attribute__ ((packed)); /* Note: the __attribute__ is irrelevant here */
请注意,
数据
字段没有提到任何大小。还要注意,这个特殊变量只能出现在结构的末尾


在C99中,这一问题在6.7.2.1.16(重点)中解释:

作为特例,具有多个命名成员的结构的最后一个元素可以 具有不完整的数组类型这称为灵活数组成员。在大多数情况下, 将忽略灵活数组成员。特别是,结构的大小就像 省略了flexible数组成员,只是它的尾部填充可能比 这一遗漏意味着。然而,当一个。(或->)运算符的左操作数为 (指向)具有灵活数组成员和正确操作数名称的结构 成员,其行为就好像该成员被替换为最长的数组(具有相同的 元素类型),不会使结构大于正在访问的对象;这个 阵列的偏移量应保持柔性阵列构件的偏移量,即使这会有所不同 从替换阵列的。如果这个数组没有元素,它的行为就像 它只有一个元素,但如果试图访问该元素,则行为未定义 元素或来生成一个指针,该指针超过它一个

或者换句话说,如果您有:

struct something
{
    /* other variables */
    char data[];
}

struct something *var = malloc(sizeof(*var) + extra);
您可以使用
[0,额外)
中的索引访问
var->data
。请注意,
sizeof(struct something)
只会给出其他变量的大小,即给
data
一个0的大小


还值得注意的是,本标准实际给出了
malloc
ing此类构造的示例(6.7.2.1.17):

标准在同一位置的另一个有趣的注释是(我的重点):

假设对malloc的调用成功,在大多数情况下,p指向的对象的行为就好像p被声明为:

struct { int n; double d[m]; } *p;
(在某些情况下,这种等效性被破坏;特别是,成员d的偏移量可能不同)

代码不是有效的C()。Linux内核显然不关心可移植性,因此它使用了大量非标准代码

他们正在做的是一个数组大小为0的GCC非标准扩展。一个符合标准的程序将编写
u8 data[];
,这意味着同样的事情。Linux内核的作者显然喜欢让事情变得不必要的复杂和非标准,如果这样做的选项暴露出来的话

在旧的C标准中,以空数组结束结构被称为“结构破解”。其他人已经在其他答案中解释了它的用途。C90标准中的结构破解是未定义的行为,可能会导致崩溃,主要是因为C编译器可以在结构的末尾添加任意数量的填充字节。此类填充字节可能会与您试图在结构的末尾“破解”的数据发生冲突


GCC早期做了一个非标准扩展,将其从未定义行为更改为定义良好的行为。C99标准随后采用了此概念,因此任何现代C程序都可以使用此功能而不存在风险。它在C99/C11中被称为灵活数组成员。

零长度数组的另一种用法是作为ASIS结构内部的命名标签t编译时结构偏移量检查

假设您有一些大的结构定义(跨越多个高速缓存行),以确保它们在起点和中间都对齐到高速缓存行边界。

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);
那么运行时断言代码将更易于维护:

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);

我不确定是否应该有一个或标记…@hippietrail,因为当有人问这个结构是什么时,他们通常不知道它被称为“flexible array member”。如果他们这样做了,他们可以很容易找到答案。因为他们没有,所以他们不能给问题贴上这样的标签。这就是为什么我们没有这样的标签。投票重新打开。我同意这不是重复,因为其他帖子都没有提到非标准“结构黑客”的组合长度为零,定义良好的C99具有灵活的数组成员。我还认为,C编程社区了解任何obsc都是有益的
#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);