【问题标题】:Difference between Undefined Behavior and Ill-formed, no diagnostic message requiredUndefined Behavior 和 Ill-formed 之间的区别,不需要诊断消息
【发布时间】:2014-04-06 11:33:19
【问题描述】:

C++ 标准为 unclear1 行为提供了大量的定义,这意味着或多或少相同,但存在细微差别。阅读this answer,我注意到措辞“程序格式错误;不需要诊断”

实现定义未指定行为的不同之处在于,前一种情况下的实现必须清楚地记录它正在做什么(在后一种情况下,它不需要),两者都是格式良好的未定义行为与未指定行为的不同之处在于程序是错误 (1.3.13)。
否则,它们都有一个共同点,即该标准对实施将做什么没有任何假设或要求。 除了 1.4/8,它指出实现可能有扩展,这些扩展不会改变格式良好的程序的行为,但根据标准是格式错误的,并且实现必须诊断这些的使用,但之后可以继续编译和执行格式错误的程序。

格式不正确的程序仅被定义为格式不正确(太棒了!)。另一方面,格式良好的程序被定义为符合语法和可诊断语义规则的程序。因此,这意味着格式错误的程序是违反语法或语义规则(或两者)的程序。换句话说,一个格式错误的程序实际上根本不应该编译(如何以任何有意义的方式翻译一个语法错误的程序?)。

我倾向于认为 erroneous 这个词也意味着编译器应该使用错误消息中止构建(毕竟,erroneous 表明存在错误) ,但 1.3.13 中的“注意”部分明确允许一些不同的东西,包括默默地忽略问题(编译器明显不会因为 UB 而破坏构建,大多数默认情况下甚至不会发出警告) .

人们可能会进一步认为 erroneous 和 ill-formed 是相同的,但如果是这种情况或该词的含义,该标准并未详细说明。

此外,1.4 规定

符合规范的实现应 [...] 接受并正确执行格式良好的程序

如果一个程序违反了不需要诊断的规则,[...] 对该程序的实施没有要求。

换句话说,一个符合规范的实现必须接受一个格式良好的程序,但它也可能接受一个格式错误的程序,甚至没有警告。除非,如果程序格式不正确因为它使用了扩展

第二段暗示任何与“不需要诊断”相关的东西意味着规范中没有要求,这意味着它几乎等同于“未定义的行为”,除了没有提及错误 em>。

因此,使用诸如“格式错误;无需诊断”之类的措辞背后的意图是什么?

“无诊断”的存在表明它与未定义的行为相同(或大部分相同?)。此外,由于实现定义未指定行为被定义为格式良好,它必须是不同的

另一方面,由于格式错误的程序破坏了语法/语义规则,它实际上不应该编译。然而,与“不需要诊断”结合使用意味着编译器将被允许在没有警告的情况下静默退出,并且之后您将无法找到可执行文件。

“格式错误;无需诊断”和“未定义行为”之间是否有区别,或者这只是同一事物的复杂同义词?


1对于集体行为缺乏更好的措辞

【问题讨论】:

  • 另见question 侧边栏中列出的有用信息。
  • @vonbrand:排名最高的答案似乎得出这样的结论,即“格式错误”意味着“诊断消息加上未定义的行为”,其推理与我的问题相同,即 1.4.2 的措辞与 1.3 相似.13(但这真的意味着它是一样的吗?)。如果我们假设这个推理是正确的,那么“格式错误;没有诊断”将是“诊断加上未定义的行为减去诊断”,所以简单地说是“未定义的行为”。
  • 具有讽刺意味的是,我刚刚意识到我链接到的问题中的那个例子(包含让我想知道的引用的那个)必须必然破坏构建: “如果不存在函数参数值,使得函数调用替换会产生一个常量表达式”。如果您需要一个编译时常量(例如,对于模板参数)并且您无法从输入中生成一个,那么除了立即因错误而中止之外,您还能做什么?当然,“无需诊断”不能真正适用于这种情况。
  • 没有。 Doing char *p = malloc(100); free(p); *p = 10; 是未定义的行为。包含这些行的程序可以是格式良好的,特别是如果某些其他约束不允许确切的事件序列。似乎唯一明确的不规范的情况来自“单一定义”规则。
  • 符合标准的实现必须接受格式良好的程序”,直至其实现“限制”。所以这是一个非常弱的“必须”。实现应该具有至少与标准中指定的一样大的限制,但这不是严格要求。

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


【解决方案1】:

标准并不总是像我们希望的那样连贯,因为 这是一个非常大的文件,由一个数字(在实践中)编写 不同的人,尽管所有的校对 确实发生了,不一致的地方就会溜走。如果是 未定义的行为(以及一般的错误),我认为有一个 对于许多最基本的事情,还有一个额外的问题 (指针等),C++ 标准的灵感来自于 C。但是 C标准认为所有错误都是未定义的 行为,除非另有说明,如 C++ 标准 试图采取所有错误都需要的观点 诊断,除非另有说明。 (虽然他们仍然 必须允许标准省略指定的情况 一种行为。)我认为这占了很多 措辞不一致。

在全球范围内,这种不一致是令人遗憾的,但总的来说,如果 标准说有些东西是错误的或格式不正确的, 那么它需要一个诊断,除非标准说它 没有,或者它是未定义的行为。在类似的东西 “格式错误;无需诊断”,“无需诊断” required" 很重要,否则,它需要 诊断。至于“格式错误;没有”之间的区别 需要诊断”和“未定义的行为”,没有。 第一个可能在代码是 不正确,第二个是运行时问题,但不是 系统的。 (一个定义的规范 规则——显然是一个编译时问题——以“then 行为未定义”。)

【讨论】:

  • 有区别。 “格式错误”意味着 程序 写得不好,“未定义行为”意味着在 运行时 可能会发生一些奇怪的事情。
  • @vonbrand 不在 C++ 标准中。没有什么可以将“未定义的行为”限制在运行时。我知道:“行为”这个词暗示了运行时。但是该标准使用自己的定义,并且在其定义的上下文中,唯一的区别是“格式错误”可能需要诊断 - 事实上,除非另有说明,否则它确实需要诊断 - 并且未定义的行为不需要不需要。
  • @david.pfx 在处理标准(或任何标准)时,您不能说“x 意味着 y”。该标准非常明确地定义了它使用的术语。特别是,有明确的“格式错误,不需要诊断”的情况,这是未定义的行为。并且违反一个定义规则,包括缺少定义,是未定义的行为,即使大多数实现在翻译(在本例中为链接)时检测到它的至少一些实例。
  • @JamesKanze:这是我对标准的阅读,虽然用的是非正式的语言。 N3337 1.3.25 表示 UB 仅适用于格式良好的程序,而 1.3.26 表示违反 ODR 不是格式良好的。 1.4 将 NDR 与 UB 区分开来。我找不到明确 UB 的“格式错误,不需要诊断”的示例。尽管您对定义充满信心,但我认为该标准在 NDR 和 UB 之间保持了明确的区别,但未能定义这种区别。
  • 在产生未定义行为的构造与格式错误的构造之间,我可以看到的唯一显着区别是,符合标准的编译器可以作为默认行为分配并记录它认为适合的任何有用含义表示未定义行为的构造,但符合标准的编译器可能无法干净地编译标准状态不正确的构造(它可能允许程序运行,但在使用默认设置时,它必须发出诊断)。也许“格式错误 + NDR”意味着不允许编译器“记录”结构......
【解决方案2】:

它应该是这样的:只要程序的特定运行不会触发未定义的行为,未定义的事物就不会引起问题。例如。空指针取消引用只会在您的特定程序运行(其特征在于其输入:I/O、时钟查询等非确定性函数等)实际执行它时破坏您的一天 - 但它会向后移动,因此它可能会表现出未定义的行为甚至在技术上达到取消引用之前。 (我认为这主要是为了允许代码重新排列。)

而格式不正确的 NDR 是实现应该在翻译过程中诊断的东西,但由于各种技术或理论限制可能无法诊断。例如。 ODR 将要求实施收集实体的所有定义并进行比较;但这是一个巨大的资源消耗。一些 NDR 的事情甚至在计算上是不可行的。当实现没有立即诊断出这些东西时,就会出现未定义的行为。

实际上,未定义的行为适用于一些不是运行时条件的奇怪情况。一些奇怪的预处理器问题会触发未定义的行为。这些很奇怪,因为它们在编译的程序中没有有意义的表示,所以不清楚是什么导致它们执行。

尽管如此,这种观点仍然可以让您合理地理解为什么会有两个术语。

【讨论】:

  • 关于你的第一句话:为什么?标准中哪里有这方面的迹象? §1.3.24 中的未定义行为 的定义明确提到了“翻译或执行”(尽管在非规范性评论中)。而一些最常见的未定义行为是由于违反了单一定义规则,这与运行时无关。
  • 听起来很合理,但是...constexpr 无法从其参数生成常量表达式的特定示例是明确的非运行时情况,并标记为“格式错误的 NDR”,坦率地说,这是不可能的(或者我太愚蠢以至于无法理解它?)...如果您在需要的地方缺少一个常量表达式,则无法继续构建(看看我的困惑来自哪里:-)) .当参数不是常量时,您将使用什么实例化模板或将枚举初始化为什么?编译器根本无法诊断。
  • 作为扩展,编译器允许使用两个外部符号的地址差异,就好像它是受某些限制的编译时常量扩展一样,这是否合法[例如用法在许多汇编程序中是合法且常见的]?如果是这样,那么可能会有一个表达式在链接时间之前无法检测到导致 UB。
猜你喜欢
  • 2015-09-10
  • 2022-08-20
  • 1970-01-01
  • 1970-01-01
  • 2013-01-10
  • 1970-01-01
  • 1970-01-01
  • 2017-02-20
  • 2018-03-11
相关资源
最近更新 更多