4

通过实验(在 Clang 和 GCC 中,使用 -O2 和 -O0)似乎在以下代码中

typedef struct foo_s { int i; int j; } foo_t;
int main(void) {
    foo_t foo = {.i = 42};
    ...

foo.j 自动为零。

是 C99 以后的保证,还是编译器特定的实现细节?

注意:我什至尝试将 0xFFs 写入堆栈下方的无效内存,位于 foo 稍后给出的地址。

更新:有几条评论指出这只是因为堆栈下方的内存恰好包含零。以下代码确保不是这种情况,并且可以证明 GCC -O0 正在清零内存。

-7 和 -6 的偏移量取决于编译器。他们需要在 Clang 中有所不同。

typedef struct foo_s { int i; int j; } foo_t;

int main(void) {
    int r;
    int *badstack0 = &r - 7;
    int *badstack1 = &r - 6;

    *badstack0 = 0xFF; // write to invalid ram, below stack
    printf("badstack0 %p, val: %2X\n", badstack0, *badstack0);
    *badstack1 = 0xEE; // write to invalid ram, below stack
    printf("badstack1 %p, val: %2X\n", badstack1, *badstack1);

    // struct test
    foo_t foo = {.i = 42};
    printf("&foo.i %p\n", &foo.i);
    printf("&foo.j %p\n", &foo.j);
    printf("struct test: i:%i j:%i\n", foo.i, foo.j);
    return 0;
}

输出:

badstack0 0x7fff221e2e80, val: FF
badstack1 0x7fff221e2e84, val: EE
&foo.i 0x7fff221e2e80
&foo.j 0x7fff221e2e84
struct test: i:42 j:0
4

2 回答 2

9

如果您提供任何初始化程序,则未明确提及的成员将被初始化,就好像它们是静态的一样。6.7.9 (19) 中的标准保证了这一点:

初始化应按初始化程序列表顺序进行,为特定子对象提供的每个初始化程序都覆盖同一子对象的任何先前列出的初始化程序;所有未显式初始化的子对象都应隐式初始化,与具有静态存储持续时间的对象相同

(重点由我添加)

如果不初始化任何成员,则所有成员的值都是不确定的。

于 2013-02-28T14:42:17.570 回答
4

C 保证只要数组/结构/联合* 的至少一个成员被显式初始化,那么所有其他成员都将被初始化,就好像它们具有静态存储持续时间一样,正如 Daniel Fischer 的回答中所引用的那样。换句话说,所有其他成员都自动设置为零或 NULL。

数组/结构/联合的存储类型无关紧要,无论它们是自动存储时间还是静态存储时间,它们都根据相同的规则进行初始化。

这不是 C99 或更高版本独有的东西,C一直有这个要求。所有符合标准的 C 编译器都遵循这个规则,它是规范的,而不是实现定义的。

这与“调试版本”归零无关。

事实上,这条规则解释了为什么你可以通过编写来将整个数组归零

int array[100] = {0}.

这段代码的意思是,“将第一个元素初始化为零,并将剩余的 99 个元素初始化,就好像它们具有静态存储持续时间一样,即也将它们设为零”。


(*) 这三种类型在 C 标准中被正式命名为“聚合类型”。

于 2013-02-28T15:15:38.897 回答