下面我对这段代码有点困惑:
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
struct test_struct {
uint8_t f;
uint8_t weird[];
};
int main(void) {
struct {
struct test_struct tst;
uint8_t weird[256];
} test_in = {};
printf("%u\n", test_in.weird[0]); // 0
test_in.tst.weird[0] = 1;
printf("%u\n", test_in.weird[0]); // 1
return 0;
}
我不知道可以这样使用struct的字段,所以我有两个问题:
- 它在C语言中是怎么叫的?
- 当然,它是如何工作的?(为什么
weird
字段被更改,而我没有直接更改它,我以为这是两个不同的字段?)
4条答案
按热度按时间nbnkbykc1#
下面我对这段代码有点困惑:
简短的回答是:该代码具有未定义行为。
它在C语言中是如何被调用的?它是如何工作的?
struct test_struct
的最后一个成员被定义为未指定长度的数组:uint8_t weird[];
此成员称为 * 灵活数组成员 *,不要与可变长度数组混淆。6.7.2类型说明符
[...]
20 作为一种特殊情况,具有多个命名成员的结构的最后一个成员可能具有不完整的数组类型;这称为“灵活数组成员”。2在大多数情况下,灵活数组成员会被忽略。3特别是,结构的大小就像灵活数组成员被省略一样,只是它的尾部填充可能比省略所暗示的要多。4但是,当一个
.
(或->
)运算子的左算子为(指向)具有灵活数组成员的结构,右操作数命名该成员,它的行为就好像该成员被最长的数组(具有相同的元素类型)所替换,这不会使结构大于被访问的对象;数组的偏移量应保持为可变数组成员的偏移量,即使这与替换数组的偏移量不同。如果此数组没有元素,它的行为就像它有一个元素一样,但如果试图访问该元素或生成一个经过它的指针,则行为未定义。weird
成员访问这些元素,访问的元素数不超过这样分配的元素数。test_in.tst.weird
的元素具有未定义的行为,访问test_in.weird
的元素也是如此。test_in.tst.weird
数组和test_in.weird
数组完全重叠,但不保证也不支持这种情况:依赖于这种别名的代码也具有未定义的行为。{}
(下一代C标准的一部分,从C++中借用),它似乎可以按预期工作,但这并不保证,对齐问题可能会导致它失败,如下面的修改版本所示:输出量:
wh6knrhe2#
实际上,在语言中有FAM之前,您声明的内容是:
q5iwbnjs3#
相反,如上面的注解部分所述,
不是可变长度数组,它要么称为未知大小的数组(cppreference),要么称为零长度数组(gcc)。
VLA的示例如下:
基于以下评论(来自cppreference -请参见提供的链接):
在结构体定义中,一个未知大小的数组可能作为最后一个成员出现(只要至少有一个其他命名成员),在这种情况下,它是一个称为灵活数组成员的特殊情况。有关详细信息,请参阅结构体(解释部分):
atmip9wb4#
提供的代码无效。
您声明了一个具有 * 灵活数组成员 * 的结构
来自C标准(6.7.2.1结构和联合说明符)
18作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这被称为“灵活阵列构件”。
从引用中可以看出,这样的成员必须是结构的最后一个元素,所以上面的结构声明是正确的。
但是,在main中,您声明了另一个未命名的结构
包含一个结构的元素作为成员,该结构具有灵活数组元素,但该元素现在不是未命名结构的最后一个元素。因此,这样的声明是无效的。
第二,你用空括号初始化一个未命名结构的对象。与C++相反,在C中你不能用空括号初始化对象。