【问题标题】:What is the point of undefined behavior? [duplicate]未定义行为的意义何在? [复制]
【发布时间】:2017-07-15 05:35:23
【问题描述】:

我可以理解保留定义的实现,以便实现它的特定人员知道最好发生什么,但为什么某些事情会是未定义的行为?为什么不直接说,还有什么是实现定义的?

【问题讨论】:

  • 实现定义意味着必须记录行为,因此必须保持一致。未定义的意思是“没有规则”。
  • @BoPersson-- 我认为 OP 正在寻找理由。
  • 基本的答案是编译器可以优化假设未定义的行为永远不会发生。其影响基本上无法预测,因此无法记录(这是“定义”所必需的)。有关示例,请参见 gcc.gnu.org/bugzilla/show_bug.cgi?id=33498。
  • 如果您要为所有情况定义行为,则必须为它们检查。其中一些无法被检查,另一些没有有意义的解决方案(“永远不要检查你不知道如何处理的错误情况”),另一些仍然太苛刻而无法检查。对于这些情况,语言标准允许行为保持“未定义”,这允许更容易、更快地实现编译器和库。 (简单示例,strcpy()。您无法检查所涉及的字符串是否正确地以零结尾。如果不是,则行为未定义。)
  • @MartinBonner 不,这绝对不是原因。只是 gcc 选择以这种方式解释它,这反过来又使它在某些情况下成为一个不太有用的编译器(想到了严格的别名违规)。

标签: c++ c language-lawyer undefined-behavior


【解决方案1】:

在很多情况下,确保实现定义的行为不可避免地会产生开销。

例如,如何使缓冲区溢出成为实现定义的行为?可能在检测到异常时抛出异常,或者终止程序?但这样做总是需要边界检查。

C++ 的设计方式几乎不会影响性能。

Go、Rust 和 Java 等语言会进行边界检查。而且他们都会产生开销来为安全付出代价。

【讨论】:

  • Rust 是否进行任何运行时检查?我以为没有。你有解释它的链接吗?
  • @rtur 如果我没记错的话,Rust 通常会在运行时进行边界检查。为了避免边界检查,您必须使用unsafe 块,并在块内调用一些unsafe 方法。但这似乎不是使用 Rust 的常用方式。
  • 然而,应该注意的是,从标准的角度来看,实现定义的行为和未定义的行为都要求实现者根据诸如目标平台和应用领域。 IDB 和 UB 之间的唯一区别是前者需要一个实现来定义一致的行为,即使在这样做无用的情况下也是如此,而后者则不需要。一些编译器编写者将 UB 视为不需要判断的指示,...
  • ...但是我在 C89 中看不到任何表明这种意图的东西,我在后来的标准中也没有看到任何东西表明许多实现定义的行为是否应该不应该继续以同样的方式对待。
【解决方案2】:

语言规范可以看作是编译器编写者和程序员之间的契约。有些事情程序员认为编译器会做(而不做)。编译器编写者假设程序员会做的事情很少。

编译器作者假定程序员编写的代码具有记录的行为或没有未定义的行为。基于此,编译器编写者可以忽略某些构造,并且与未定义这些构造的情况相比,可以使编译的程序更快。规范中记录了一些(尽可能多地)未定义的构造,以使程序员了解此类构造。

这种未定义行为的存在是为了减少规范实施中不必要的复杂性,有时还会为优化留出空间。

【讨论】:

  • C 标准的不同寻常之处在于,在编写它的时候,C 是一个方言家族,这些方言已经演变为不同且经常不兼容的目的。 C 标准从未尝试包含使编译器适用于系统编程等目的所需的所有特性,而是期望那些希望他们的编译器适用于此类目的的人将支持适合此类目的的行为,无论标准是否要求他们到与否。
  • @supercat 然后出现了许多有用且奇怪的扩展。
  • 确实如此。我的观点是,如果针对特定平台和编程领域(例如微型计算机系统编程)的编译器一致支持某些操作的有用行为,但标准的作者决定不要求所有编译器都支持它,则标准不会代表程序员不使用该功能的合同协议。
猜你喜欢
  • 1970-01-01
  • 2021-04-21
  • 1970-01-01
  • 2011-10-30
  • 1970-01-01
  • 2011-11-02
  • 2021-11-15
相关资源
最近更新 更多