【问题标题】:Are unmentioned struct fields *always* initialised to zero (i.e. when the struct is on the stack)?未提及的结构字段*总是*初始化为零(即当结构在堆栈上时)?
【发布时间】:2013-02-28 14:39:59
【问题描述】:

通过实验(在 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 稍后给出的地址。

更新:有几个 cmets 指出这只是因为堆栈下方的内存恰好包含零。以下代码确保不是这种情况,并可能证明 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

【问题讨论】:

    标签: c struct initialization


    【解决方案1】:

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

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

    (我添加的重点)

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

    【讨论】:

    • 值得补充的是,在特定情况下,OP 只是在程序开始时有一个归零的堆栈区域,并且 j 成员具有值 0
    • 不,即使堆栈区域没有归零,OP 也有 foo_t foo = {.i = 42}; 一个成员的显式初始化。因此,其他成员必须初始化为 0,就好像它是 static int j;
    • @Daniel Fischer IBM 是否说相反? publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/… "temp_address.postal_code 取决于 temp_address 变量的存储类;如果是静态的,则值为 NULL。"
    • 他们确实说它取决于存储类,但这与标准相矛盾(顺便说一句,该部分是 C99 中的 6.7.8)。由于struct 已初始化,没有显式初始化器的成员应与具有静态存储持续时间的对象一样隐式初始化。在 IBM 站点上的示例中,它是一个指针,因此必须将其初始化为空指针。
    【解决方案2】:

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

    array/struct/union的存储类型无关紧要,不管是自动存储还是静态存储,都按照相同的规则进行初始化。

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

    这与“调试发布”零输出无关。

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

    int array[100] = {0}

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


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

    【讨论】:

    • Daniel Fischer 的回答应该被认为是正确的。我的回答仅在 OP 编辑​​后添加了其他详细信息。
    • 如果联合的第一个成员不是最大的,那么标准对其他成员的初始化有什么规定?例如,如果空指针、浮点零和整数零都具有不同的 32 位表示,那么声明 union {void *moe[1]; float larry[2]; unsigned char curly[12];} STOOGE = {0}; 会产生什么效果?显然,curly 的前四个字节应该包含 unsigned char 表示一个空指针,但其余的呢?
    • @supercat 该标准在联合和其他“聚合”类型之间没有区别,请参阅 C11 6.7.9/21。并且初始化规则递归地适用于所有成员。在您的示例中,第一个数组成员的第一项将被显式初始化为空指针,然后是其余所有项,就好像它们具有静态存储持续时间一样。也就是说,第一个数组的其余项目,然后是所有其他数组成员。一切都将设置为零。
    • 什么样的零?指针数组是否会被初始化为空指针,而其他所有内容都会被初始化为全零,还是什么?
    • @supercat 它们将被初始化,就好像它们具有静态存储持续时间一样。阅读C11 6.7.9/10。
    猜你喜欢
    • 2018-04-06
    • 2017-05-27
    • 1970-01-01
    • 2016-08-13
    • 1970-01-01
    • 1970-01-01
    • 2016-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多