【问题标题】:Is this code really undefined, as Clang seems to indicate?正如 Clang 似乎表明的那样,这段代码真的是未定义的吗?
【发布时间】:2015-05-08 11:16:48
【问题描述】:

我在使用单元测试库 Catch 的项目中打开了-fsanitize=undefined。来自 Catch 的一行被此标志指示为导致未定义的行为。我设法做了一个孤立的例子:

#include <iomanip>
#include <sstream>

int main()
{
    std::ostringstream os; 
    os << "0x" << std::setfill('0') << std::hex;
}

编译:

clang++ -fsanitize=undefined main.cpp

如果我运行它,将给出以下打印:

/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/ios_base.h:96:24: runtime error: load of value 4294967221, which is not a valid value for type 'std::_Ios_Fmtflags'
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/ios_base.h:76:67: runtime error: load of value 4294967221, which is not a valid value for type 'std::_Ios_Fmtflags'

这发生在我的 clang 3.6.0 和一个使用 clang 3.4-1ubuntu3 的朋友身上。我在 gcc 版本4.9.2 上不会发生这种情况@

那么这里是什么?这段代码真的很糟糕,还是在 clang 的末尾有什么可疑的地方?

【问题讨论】:

标签: c++ clang undefined-behavior libstdc++ sanitizer


【解决方案1】:

这是bug in libstdc++,来自cfe-dev 邮件列表线程,标题为-fsanitize=undefined 和共享库 说:

这是 libstdc++ 中的一个错误。您将能够使用 sanitizer 黑名单文件,一旦 Will 的补丁登陆,但对于 现在,手动过滤它们可能是您的最佳选择。

这是一个修复它的补丁;我会考虑把这个推到 libstdc++ 将在接下来的几天内上游。 [...]

正如我在 cmets 中 dyp 所指出的,看到clang 使用libstdc++ 而不是libc++ 的系统并不少见,如果我们通过Coliru explicitly using libstdc++ 通过-stdlib=libstdc++ 对此进行测试,我们确实可以重现问题。

以下libstdc++ 错误报告:bad enum values computed by operator~ in ios_base.h 涵盖了这个问题并说:

为 ios_base.h 中的枚举定义的重载 operator~s 有以下形式:

Enum operator~(Enum e) { return Enum(~static_cast<int>(e)); }

~ 创建枚举值范围之外的值 类型,因此转换回 Enum 类型具有未指定的值(请参阅 [expr.static.cast]p10),实际上它会产生一个枚举值 在 Enum 类型的可表示值范围之外,所以 行为未定义。

供参考[expr.static.cast]p10 说:

整数或枚举类型的值可以显式转换为枚举类型。值为 如果原始值在枚举值 (7.2) 的范围内,则保持不变。否则,结果 值未指定(并且可能不在该范围内)。浮点类型的值也可以转换 为枚举类型。结果值与将原始值转换为底层证券相同 枚举类型(4.9),然后是枚举类型。

正如 hvd 所说,这是形式上未指定的行为,但 Richard 指出,实际上最终是未定义的行为。

T.C.指出 DR 1766: Values outside the range of the values of an enumeration 将其从未指定行为更改为未定义行为:

虽然 issue 1094 澄清了枚举类型的表达式的值在转换为枚举类型后可能不在枚举值的范围内(参见 5.2.9 [expr.static.cast] 第 10 段) ,结果只是一个未指定的值。考虑到未定义的行为使表达式变得非常数这一事实,这可能应该被加强以产生未定义的行为。另见 9.6 [class.bit] 第 4 段。

新的措辞出现在N4431的标准草案中。

【讨论】:

  • 哦,确实很有趣。太糟糕了,手动过滤它们消除了使用 gdb 打破此类错误以获取用于修复它们的堆栈跟踪的可能性。
  • 强制转换产生未指定值的事实意味着行为定义。如果行为未定义,则标准会说行为未定义。 (但是,它仍然不是有用的行为,因此仍应更改代码。)
  • @hvd 好吧,Richard 说的是in practice it produces an Enum value outside the range of representable values for the Enum type, so behavior is undefined,当消毒剂看到它时,可能无法区分。
  • @Tobias 大概使用libc++ 而不是libsdc++ 应该可以解决这个问题。
  • @hvd CWG 1766 已完全未定义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-26
  • 2011-06-10
  • 2014-07-18
  • 2019-01-11
  • 1970-01-01
  • 1970-01-01
  • 2013-04-01
相关资源
最近更新 更多