定义未定义的行为

时间:2012-12-28 17:27:43

标签: c++ c undefined-behavior

是否存在C ++(和/或C)的实现,可以保证在任何时候调用未定义的行为,它会发出错误信号?显然,这样的实现可能不如标准C ++实现那么高效,但它可能是一个有用的调试/测试工具。

如果不存在这样的实施,那么是否存在任何无法实施的实际原因?或者只是没有人完成工作来实现它?

编辑: 为了使这更加精确:我希望有一个允许我进行断言的编译器,用于给定的运行<运行完成的C ++程序的/ em> ,该运行的任何部分都没有涉及未定义的行为。

4 个答案:

答案 0 :(得分:3)

是的,没有。

我相当确定,出于实际目的,实现可以使C ++成为一种安全的语言,这意味着每个操作都有明确定义的行为。当然,这会带来巨大的开销,并且可能存在一些根本不可行的情况,例如多线程代码中的竞争条件。

现在,问题是这不能保证您的代码在其他实现中定义!也就是说,它仍然可以调用UB。例如,请遵守以下代码:

int a;
int* b;

int foo() {
  a = 5;
  b = &a;
  return 0;
}

int bar() {
  *b = a;
  return 0;
}

int main() {
  std::cout << foo() << bar() << std::endl;
}

根据标准,调用foobar的顺序取决于实现的决定。现在,在安全实施中,必须定义此订单,可能是从左到右的评估。问题是评估从右到左调用UB,直到你在不安全的实现上运行它才会被捕获。安全实现可以简单地编译评估顺序的每个排列或进行一些静态分析,但这很快变得不可行并且可能不可判定。

总而言之,如果存在这样的实现,它会给你一种虚假的安全感。

答案 1 :(得分:2)

新的C标准在新附件L中有一个有趣的清单,标题为“可分析性”。它谈到的是所谓的关键UB 的UB。其中包括:

  • 在其生命周期之外引用一个对象(6.2.4)。
  • 指针用于调用类型与引用不兼容的函数 输入
  • 程序尝试修改字符串文字

所有这些都是不可能或很难捕获的UB,因为它们通常无法在编译时完全测试。这是因为有效的C(或C ++)程序由几个可能彼此不太了解的编译单元组成。例如,如果一个程序将指向字符串文字的指针传递给具有char*参数的函数,或者甚至更糟的是,从静态变量中抛弃const - ness的程序。

答案 2 :(得分:2)

为大量顺序C检测大量未定义行为的两个C解释器是KCCFrama-C's value analysis。它们都用于确保自动生成的,自动减少的随机C程序适用于C编译器中的report错误。

来自KCC的网页:

  

这项工作的主要目标之一是能够检测未定义的   程序(例如,读取无效内存的程序)。

C语言的第三个翻译是CompCert的翻译模式(a writeup)。这个检测到认证的C编译器CompCert的输入语言中未定义的所有行为。 CompCert的输入语言本质上是C,但它定义了一些在标准中未定义的行为(例如,带符号的算术溢出被定义为计算2的补码结果)。

事实上,在这个答案中提到的所有三个口译员都以实用主义的名义做出了艰难的选择。

答案 3 :(得分:-1)

将某些内容定义为“未定义的行为”的重点是避免在编译器中检测到这种情况。它以这种方式定义,因此可以为各种各样的平台和体系结构构建编译器,并且硬件和软件不必具有“仅检测未定义的行为”的特定功能。想象一下,你有一个内存子系统无法检测你是否正在写入实际内存 - 编译器或运行时系统如何检测你刚刚完成somepointer = rand(); *somepointer = 42;

您可以检测到某些情况。但是要求检测到ALL,会让生活变得非常困难。

考虑到原始问题中的编辑:我仍然不认为这在C中实现是合理的。几乎任何事情都有很多自由(指向几乎任何东西,这些指针可以被转换,索引,重新计算) ,以及各种其他方式),并将能够导致各种未定义的行为。 C here中列出了所有未定义的行为 - 它列出了186种不同行为的不同情况,从反斜杠作为文件的最后一个字符(可能导致编译器错误,但未定义为一个)到“bsearch或qsort函数调用的比较函数不一致地返回排序值”。

你究竟如何编写一个编译器来检查传入bsearch或qsort的函数是否一致地排序值?当然,如果传递给比较函数的数据是简单类型,例如整数,那么它并不困难,但如果数据类型是复杂类型,例如

struct {
    char name[20];
    char street[20];
    int age;
    char post_code[10];
};

并且程序员决定按照升序名称,升序街道,降序年龄和升序邮政编码对数据进行排序?如果那是你想要的,但不知何故,代码搞砸了,后期代码比较会返回一些不一致的结果,事情就会出错,但要正式检查这种情况是非常困难的。还有很多其他同样模糊和复杂的东西。当然,你的代码可能不会对名称和地址等进行排序,但有些人可能会在某些时候写出类似的东西。