【问题标题】:Could invoking a void statement cause undefined behavior?调用 void 语句会导致未定义的行为吗?
【发布时间】:2013-10-02 13:27:58
【问题描述】:

想象一下:

int X;

X = X;

这将是未定义的行为,因为

1 在以下情况下行为未定义:

[...]

具有自动存储持续时间的对象的值在它被使用时使用 不确定(6.2.4、6.7.8、6.8)。

但是这个呢?

int X;

X;

在引用引用时调用X; 是否允许编译器导致未定义的行为?或者这不算作 X 被“使用”?

【问题讨论】:

  • 你已经用 c99 标记了这个问题。 C 1999 和 C 2011 的答案是不同的。你只想要 C 1999 吗?
  • @Eric Postpischil 我引用了 ISO/IEC 9899:TC3,即 c99。但如果两者的行为不同,我更希望得到两者的答案

标签: c c99 undefined-behavior c11


【解决方案1】:

X = X;

以上是未定义的行为。因为X 没有初始化。编译器至少应该对此产生警告。

标准规定 6.3.2.1p2:

如果左值指定了一个可以使用寄存器存储类声明的具有自动存储持续时间的对象(从未使用过它的地址),并且该对象未初始化(未使用初始化程序声明并且没有对其执行分配在使用之前),行为是未定义的。

但是:

X;

上面类似:

1212342413;

X 将评估为某个表达式。

【讨论】:

  • 谢谢!但是您能否也说明一下标准对X; 的看法?因为我不确定我能在多大程度上理解标准中引用的问题中“使用”的含义。
  • 您引用的是 C11 但问题被标记为 c99
  • 编译器为什么要产生警告?行为只是没有定义,它不是违反约束。
  • "编译器至少应该对此产生警告。" -- 我同意,它应该,但标准没有这样说。
【解决方案2】:

在 C 1999 中,使用未初始化的对象并不是直接错误。 (您对附件 J 的引用不是标准的规范部分;它们只是提供信息。)具有自动存储持续时间的未初始化对象具有不确定的值。对于某些对象,该值可能是陷阱表示,因此使用它可能会导致未定义的行为。

但是,对于某些对象,可以确定未初始化的对象不能具有陷阱值。例如,unsigned char 不能有陷阱值,stdint.h 中定义的精确宽度有符号整数类型不能有陷阱值(因为它们是没有填充位的二进制补码)。对于其他类型,可能是您的 C 实现定义的属性导致它们没有陷阱值。使用未初始化的 int X 并没有在所有 C 1999 实现中定义行为(但在某些实现中),但使用未初始化的 unsigned char X 可以。

在 C 2011 中,此文本在 6.3.2 2 中添加:“如果左值指定了一个可以使用 register 存储类声明的自动存储持续时间的对象(从来没有它的地址采取),并且该对象未初始化(未使用初始化程序声明并且在使用之前未对其执行任何赋值),则行为未定义。”因此,在 C 2011 中,X = X;X; 都有未定义的行为。


历史/背景:

C 2011 更改支持惠普机器,该机器具有用于某些寄存器的特殊标志,指示寄存器内容是否有效。如果在内容无效的情况下使用了寄存器,机器会产生异常。因此,如果编译器将unsigned char XX分配给这样的寄存器,即使没有unsigned char陷阱值,在无效时使用该寄存器也可能导致机器异常。

【讨论】:

  • 一个小问题:<stdint.h> 中定义的 signed 精确宽度整数类型是二进制补码。 uint32_t 和朋友不是。
  • 如果附录J中提到的未定义行为语句不规范,那么究竟表达了什么?我现在有点困惑。
  • @KeithThompson:已更新。确切宽度的无符号类型是否应该没有填充位?那里没有说。
  • @Zaibis:标准的信息部分应该是有帮助的。通常他们是。例如,将所有未定义的行为或序列点收集在一个地方是很好的,这样您就可以轻松地查看它们。但是,当标准的规范性部分与信息性部分发生冲突时,规范性部分具有权威性。
  • 啊,好吧,所以我的引用是正确的,除非规范部分说“在以下情况下不正确[...]”我说对了吗?
猜你喜欢
  • 2019-08-02
  • 2014-08-11
  • 2016-09-04
  • 1970-01-01
  • 1970-01-01
  • 2017-01-30
  • 2013-04-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多