某些常见的编程语言,最著名的是C和C ++,具有强烈的未定义行为概念:当您尝试执行某些不希望使用的操作时,这会导致未定义行为。
如果发生未定义的行为,则允许编译器执行所需的任何操作(包括不执行任何操作,“时间旅行”等)。
我的问题是:为什么存在未定义行为的概念?据我所知,如果不是在使用未按预期使用的操作的情况下导致使用未按预期方式使用的操作,而是使用了原本打算使用的操作,则会导致大量错误,无法在一个版本的编译器上运行的程序停止运行等等。 编译错误。
为什么不是这样?
答案 0 :(得分:11)
为什么存在这种不确定行为的概念?
为了允许在各种不同的计算机体系结构上尽可能高效地实现语言/库(在C的情况下,也许是C,同时使实现保持简单)。
如果不是导致未定义的行为,而是在其预期用途之外使用这些操作会导致编译错误
在大多数未定义行为的情况下,不可能或通常无法证明资源昂贵地证明所有程序在编译时都存在未定义行为。
某些案例可以为 some 程序证明,但是不可能指定其中哪些案例是详尽无遗的,因此标准不会尝试这样做所以。但是,某些编译器足够聪明,可以识别UB的一些简单情况,并且这些编译器会警告程序员。示例:
int arr[10];
return arr[10];
该程序的行为不确定。我测试过的特定版本的GCC显示:
警告:数组下标10超出“ int [10]” [-Warray-bounds]的数组范围
忽略这样的警告绝不是一个好主意。
具有未定义行为的更典型的替代方法是在这种情况下定义已定义的错误处理,例如引发异常(例如,比较Java,访问空引用会引发类型为java.lang.NullPointerException
的异常)。但是,检查行为是否良好的前提条件要比不检查行为慢。
通过不检查先决条件,该语言为程序员提供了自己证明正确性的选项,从而避免了在证明不需要的程序中进行检查的运行时开销。确实,这种权力负有重大责任。
如今,可以通过使用工具(example)来减轻证明程序的良好定义的负担,这些工具添加了一些运行时检查,并在检查失败时巧妙地终止了程序。
答案 1 :(得分:8)
存在未定义的行为主要是为了使编译器有优化的自由。例如,它允许编译器执行的一件事是在某些事情不会发生的假设下进行操作(而不必首先证明它们不会发生,这通常是非常困难或不可能的)。通过允许它假设某些事情不可能发生,编译器可以消除/不必生成代码,否则这些代码需要考虑某些可能性。
答案 2 :(得分:-4)
未定义行为主要基于要在其上运行的目标。编译器对此程序的动态行为或静态行为概不负责。编译器检查仅限于语言规则,一些现代编译器也进行某种程度的静态分析。
一个典型的例子是未初始化的变量。由于C的语法规则而存在,可以在其中声明没有初始值的变量。一些编译器将0分配给此类变量,而另一些编译器仅将mem指针分配给该变量并像这样离开。如果程序不初始化这些变量,则会导致未定义的行为。