在计算机程序设计中,未定义行为(英语:undefined behavior)是指行为不可预测的计算机代码。这是一些编程语言的一个特点,最有名的是在C语言中。[1]在这些语言中,为了简化标准,并给予实现一定的灵活性,标准特别地规定某些操作的结果是未定义的,这意味着程序员不能预测会发生什么事。
例如,在C语言中,在任何自动对象被初始化之前,通过非字符类型的左值表达式读取这个变量存储的值会产生未定义行为,除以零或访问数组定义的界限之外的元素(参见缓冲区溢出)也会产生未定义行为。在一般情况下,之后的任何行为是不确定的;甚至只要程序的执行存在未定义行为,在引起未定义行为操作发生之前也可能不要求保证程序的行为可预测(如ISO C++)[2]。特别地,标准从来没有要求编译器判断未定义行为,因此,如果程序调用未定义行为,可能会成功编译,甚至一开始运行时没有错误,只会在另一个系统上,甚至是在另一个日期运行失败。当一个未定义行为的实例发生时,正如语言标准所说,“什么事情都可能发生”,也许什么都没有发生。
和未指定行为(unspecified behavior)不同,未定义行为强调基于不可移植或错误的程序构造,或使用错误的数据。一个符合标准的实现可以在假定未定义行为永远不发生(除了显式使用不严格遵守标准的扩展)的基础上进行优化,可能导致原本存在未定义行为(例如有符号数溢出)的程序经过优化后显示出更加明显的错误(例如死循环)。因此,这种未定义行为一般应被视为bug。
C和C++的未定义行为的一些例子[编辑]
char * p = "wikipedia"; // C++11中错误,C++98/C++03不推荐使用 p[0] = 'W'; // 未定义行为
char p[] = "wikipedia"; /* 正确 */ p[0] = 'W';
在C++可以使用标准模板库中的string类型,如下所示:
std::string s = "wikipedia"; /* 正确 */ s[0] = 'W';
除以零会导致未定义行为。根据 IEEE 754,float、double和long double类型的值除以零的结果是无穷大或NaN:[4]
return x/0; // 未定义行为
某些指针操作可能导致未定义行为:[5]
int arr[4] = {0, 1, 2, 3}; int* p = arr + 5; // 未定义行为
到达返回数值的函数(除main函数以外)的结尾,而没有一个return语句,会导致未定义行为:
int f() { } /* 未定义行为 */
《C程序设计语言》在第2.12节引用下面的代码作为未定义行为的例子:
printf("%d %d\n", ++n, power(2, n)); /* 未定义行为 */
以及
a[i] = i++; /* 未定义行为 */
标准库可能指定未定义行为,例如:
int x = 1; printf("%d\n", &x); /*未定义行为:%d预期int类型的实际参数*/ printf("%p\n", &x); /*未定义行为:%p预期void*类型的实际参数*/ printf("%p\n", (void*)&x); /*%p和void*类型的实际参数匹配,不在此引发未定义行为*/