c - 完整后跟子对象未定义行为的部分初始化?

标签 c initialization undefined-behavior

考虑以下结构初始化:

#include<stdio.h>

struct bar {
    int b;
    int a;
    int r;
};

struct foo {
    struct bar bar;
};

int main(int argc, char **argv) {
    struct bar b = {1, 2, 3};
    struct foo f = {.bar = b, .bar.a = 5 };
    // should this print "1, 5, 3", "1, 5, 0", or "0, 5, 0"?
    // clang on Mac prints "1, 5, 3", while gcc on Ubuntu prints "0, 5, 0" 
    printf("%d, %d, %d\n", f.bar.b, f.bar.a, f.bar.r);

    return 0;
}

C11 标准似乎在描述第 6.7.9 节中预期的行为方面做得很差,但似乎认为它做得很合理,因为我没有看到任何关于未定义行为的警告两种情况。

在实践中,似乎这种行为要么没有标准化,要么至少被一个常见的编译器违反了标准,Mac 上的 clang/llvm 8.0.0 生成“1、5、3”,而 gcc 5.4 在 Ubuntu 上产生“0、5、0”。

根据C标准,此时f.bar.bf.bar.r应该定义好,还是这个初始化结果是undefined或unspecified行为?

最佳答案

The C11 standard seems to do a quite poor job of describing what behavior should be expected here in section 6.7.9,

标准语可能难以阅读,但我不认为标准的这个领域在这方面比预期的更糟糕。

but seems to think it's doing a reasonable job, as I don't see any warnings regarding undefined behavior in this case either.

标准不需要明确声明未定义的行为。事实上,该标准包含一个笼统的声明,即只要它没有为给定代码段定义行为,该代码的行为就是未定义的。尽管如此,我确实认为第 6.7.9 节非常全面地涵盖了这个领域。主要开放区域是这样的:

The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.

(C2011, 6.7.9/23)

这对您的示例没有任何问题。

In practice, it seems the behavior is either not standardized or the standard is violated by at least one common compiler, with clang/llvm on a Mac producing "1, 5, 3", and gcc on Ubuntu producing "0, 5, 0".

我完全准备好相信其中一个或另一个在这个领域是不合格的。但是,还要注意编译器版本和编译选项——它们可能针对不同版本的标准进行编译,有或没有扩展。

According to the C standard, should f.bar.b and f.bar.r well defined at this point, or does this initialization result in undefined or unspecified behavior?

如果一个对象的声明有一个关联的初始值设定项,那么整个对象都会被初始化,此外,生成的初始值由标准明确定义,但要遵守 6.7.9/23 中的警告。至于您的示例中符合要求的实现所需的初始值,关键规定如下:

The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.

(C2011,6.7.9/19;已强调)

Each designator list begins its description with the current object associated with the closest surrounding brace pair. Each item in the designator list (in order) specifies a particular member of its current object and changes the current object for the next designator (if any) to be that member. The current object that results at the end of the designator list is the subobject to be initialized by the following initializer.

(C2011,6.7.9/18;已强调)

If the aggregate or union contains elements or members that are aggregates or unions, these rules apply recursively to the subaggregates or contained unions.

(C2011, 6.7.9/20)

因此,给定 f 的初始化器,

    struct foo f = {.bar = b, .bar.a = 5 };

我们首先根据 6.7.9/19 的要求处理元素 .bar = b。它包含一个指示符列表,指定类型为 struct barfoo.b 作为要从以下初始化程序初始化的对象。此初始化程序根据 6.7.9/13 执行“具有兼容结构或 union 类型的单个表达式”的选项,因此 f.bar 的初始值是 b 的值,受后续初始化程序的部分或全部覆盖。

接下来我们处理第二个元素,.bar.a = 5。这会根据 6.7.9/18 初始化 f.bar.a 并且该子对象,覆盖由根据 6.7.9/19 的先前初始化程序指定的初始化。

符合初始化的结果从而导致打印

1, 5, 3

GCC 似乎在处理第二个初始化程序时重新初始化所有 f.bar 而不是仅 f.bar.a 而失败。

关于c - 完整后跟子对象未定义行为的部分初始化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40920714/

相关文章:

C 指针错误 -> 表达式必须是可修改的左值

c - 寻找最大的值(value)

用 C 创建矩阵微分方程

c++ - 为什么C++不允许派生类在初始化列表中使用基类成员?

php - 初始化数组以在循环外使用 mysql 输出

c++ - 方便的 C++ 结构初始化

c - 正在使用未初始化变量 UB 的地址吗?

c - ACR122U 发送直接命令返回 -2 和段错误

c++ - 重置未定义行为后是否使用 shared_ptr?

c - C 中意外的位移行为