是否存在任何C++(和/或 C)的实现来保证任何时候调用未定义的行为,它都会发出错误信号?显然,这样的实现不如标准 C++ 实现高效,但它可能是一个有用的调试/测试工具。
如果不存在这样的实现,那么是否有任何实际原因导致无法实现?还是只是没有人完成实施它的工作?
编辑:为了更精确一点:我希望有一个编译器允许我做出断言,对于运行到完成的 C++ 程序的给定运行,该运行的任何部分都不涉及未定义的行为。
是否存在任何C++(和/或 C)的实现来保证任何时候调用未定义的行为,它都会发出错误信号?显然,这样的实现不如标准 C++ 实现高效,但它可能是一个有用的调试/测试工具。
如果不存在这样的实现,那么是否有任何实际原因导致无法实现?还是只是没有人完成实施它的工作?
编辑:为了更精确一点:我希望有一个编译器允许我做出断言,对于运行到完成的 C++ 程序的给定运行,该运行的任何部分都不涉及未定义的行为。
是的,没有。
我相当肯定,出于实际目的,实现可以使 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;
}
根据标准,调用的顺序foo
由bar
实现决定。现在,在一个安全的实现中,必须定义这个顺序,可能是从左到右的评估。问题是评估从右到左调用 UB,直到您在不安全的实现上运行它才会被捕获。安全的实现可以简单地编译评估顺序的每个排列或进行一些静态分析,但这很快就会变得不可行并且可能无法确定。
所以总而言之,如果存在这样的实现,它会给你一种错误的安全感。
新的 C 标准在新的附录 L 中有一个有趣的列表,粗略的标题为“可分析性”。它谈到了所谓的关键 UB UB。其中包括:
所有这些都是不可能或很难捕获的 UB,因为它们通常无法在编译时完全测试。这是因为一个有效的 C(或 C++)程序是由几个相互不了解的编译单元组成的。例如,如果一个程序将一个指向字符串文字的指针传递给一个带有char*
参数的函数,或者更糟糕的是,一个程序会const
从静态变量中丢弃 -ness。
KCC 和Frama-C 的 value analysis是两个 C 解释器,它们可以检测大量顺序 C 子集的一大类未定义行为。它们都用于确保自动生成、自动减少的随机 C 程序适合报告C 编译器中的错误。
从 KCC 的网页:
这项工作的主要目标之一是能够检测未定义的程序(例如,读取无效内存的程序)。
C 方言的第三种解释器是 CompCert 的解释器模式(a writeup)。这个检测所有在经过认证的 C 编译器 CompCert 的输入语言中未定义的行为。CompCert 的输入语言本质上是 C,但它呈现定义的一些在标准中未定义的行为(例如,有符号算术溢出被定义为计算 2 的补码结果)。
事实上,这个答案中提到的三位口译员都以实用主义的名义做出了艰难的选择。
将某些东西定义为“未定义行为”的全部意义在于避免在编译器中检测到这种情况。它是这样定义的,因此可以为各种平台和架构构建编译器,并且硬件和软件不必具有“只是为了检测未定义的行为”的特定功能。想象一下,您有一个内存子系统,无法检测您是否正在写入真实内存 - 编译器或运行时系统将如何检测您刚刚完成somepointer = rand(); *somepointer = 42;
您可以检测到一些情况。但是要求检测到 ALL 会让生活变得非常困难。
鉴于原始问题中的编辑:我仍然认为这在 C 中不太可能实现。几乎可以自由地做任何事情(制作指向几乎任何东西的指针,这些指针可以转换、索引、重新计算等等其他事物的方式),并且将能够导致各种未定义的行为。这里有一个 C 语言中所有未定义行为的列表- 它列出了 186 种不同的未定义行为情况,从作为文件最后一个字符的反斜杠(可能导致编译器错误,但未定义为一个)到“比较函数bsearch 或 qsort 函数调用时返回的排序值不一致”。
您到底如何编写编译器来检查传递给 bsearch 或 qsort 的函数是否一致地对值进行排序?当然,如果传入比较函数的数据是简单类型的,比如整数,那也没那么难,但是如果数据类型是复杂的类型,比如
struct {
char name[20];
char street[20];
int age;
char post_code[10];
};
程序员决定按姓名升序、街道升序、年龄降序和邮政编码升序对数据进行排序?如果这就是你想要的,但不知何故代码搞砸了,并且后代码比较返回了一些不一致的结果,事情就会出错,但很难正式检查这种情况。还有很多其他的同样晦涩难懂和复杂。当然,您的代码可能不会对名称和地址等进行排序,但有人可能会在某个时候写出类似的东西。