为什么初始化值本身不会在 C 中抛出任何错误?

Why initializing a value by itself is not throwing any error in C?

提问人:Ridwan Faiz 提问时间:3/12/2022 最后编辑:Ridwan Faiz 更新时间:3/12/2022 访问量:81

问:

我在练习 C 语言作业时提出了这个问题。当我尝试使用其名称(标识符)初始化任何变量时,它甚至不存在,不会抛出任何类型的错误。

int x = x;

据我所知,赋值运算符的关联性是从右到左。因此,当我使用甚至不存在的右值初始化变量时,以下代码应该会抛出错误。相反,它正在为它分配某种垃圾值。为什么会这样?

C 初始化 赋值运算符

评论

2赞 pmg 3/12/2022
int x /* x exists now though it hasn't been assigned a value yet */ = x /* UB: using garbage value (should work "sanely" on any current computer) for initialization */;
0赞 stark 3/12/2022
编译器仅检查语法。这是一个有效的声明。由程序员决定是否正确。
1赞 Eric Postpischil 3/12/2022
@stark:编译器不仅检查语法。

答:

0赞 mariolu 3/12/2022 #1

在 c 标准中,将值设置为 本身是未定义的行为。追加编译器选项后,出现警告。-Winit-self

评论

0赞 Ridwan Faiz 3/12/2022
如果它是无效的,那么为什么它没有产生任何类型的错误?
0赞 Goswin von Brederlow 3/12/2022
更糟糕的是:为什么它甚至不发出警告?(gcc -O2 -W -墙 -迂腐)
0赞 Goswin von Brederlow 3/12/2022
为自己设置一个值是完全可以的,对于某些类型来说甚至是有意义的。例如,在嵌入式系统中输出从 UART 读取的字符为:。这里的问题是右侧未初始化。*UART_DR = *UART_DR;x
0赞 Eric Postpischil 3/12/2022
它不是“编译器无效操作”。C 标准没有定义使用未采用其地址的未初始化自动对象的行为,但它也没有施加要求编译器对其进行诊断的约束。
0赞 mariolu 3/12/2022
添加此编译器选项后,将出现警告。-Winit-self
0赞 Eric Postpischil 3/12/2022 #2

GCC 11.2 确实会在使用 -Wuninitialized -Winit-self 时诊断出此问题。

我怀疑可能被用作成语1,表示“我知道我在做什么;不要警告我未初始化“,因此它被排除在 之外,但提供了单独的警告。int x = x;x-Wuninitialized-Winit-self

请注意,虽然 的行为不是由 C 标准定义的(如果它位于函数内部并且没有采用 的地址),但它也不违反 C 标准的任何约束。这意味着不需要编译器来诊断问题。选择发出警告消息是 C 实现的选择和质量问题,而不是 C 标准的规则。int x = x;x

Apple Clang 11.0 即使使用 .我怀疑这是一个错误(如果不是偏离 C 标准的规则,也偏离了作者想要的),但也许有一些原因。int x = x;-Wuninitialized -Winit-self

考虑如下代码:

int FirstIteration = 1;
int x;
for (int i = 0; i < N; ++i)
{
    if (FirstIteration)
        x = 4;
    x = foo(x);
    FirstIteration = 0;
}

编译器可能会观察到 在 中,因此可能未在 中初始化的原因。编译器可能被设计为在这种情况下发出警告消息,并且无法推断使用在 中使用之前始终初始化的保证。作为作者的断言,他们故意以这种方式设计代码,为他们提供了一种抑制虚假警告的方法。x = 4;ifxfoo(x)FirstIterationxfoo(x)int x = x;

脚注

1 有几个成语用来告诉编译者作者故意使用一些结构,这通常是一个错误。例如,是完全定义的 C 代码,它设置为 3 并且始终计算为 true,但这通常是一个预期的错误。该代码在 C 语义中是相同的,但额外括号的存在是用于表示“我故意使用赋值”的惯用语,因此编译器可能会警告 for ,但不会警告 .if (x = 3) …xifx == 3if ((x = 3)) …if (x = 3) …if ((x = 3)) …