在计算机程序设计中，**未定义行为**（英語：）是指执行某种计算机代码所产生的结果，这种代码在当前程序状态下的行为在其所使用的语言标准（英语：）中没有规定。常见于翻译器对源代码存在某些假设，而执行时这些假设不成立的情况。

一些编程语言中，某些情况下存在未定义行为，以C和C++最为著名。在这些语言的标准中，规定某些操作的语义是*未定义的*，典型的例子就是程序错误的情况，比如越界访问数组元素。标准允许语言的具体实现做这样的假设：只要是符合标准的程序代码，就不会出现任何类似的行为。具体到 C/C++ 中，编译器可以选择性地给出相应的诊断信息，但没有对此的强制要求：针对未定义行为，语言实现作出任何反应都是正确的，类似于数字逻辑中的无关项（英语：）。虽然编译器实现可能会针对未定义行为给出诊断信息，但保证编写的代码中不引发未定义行为是程序员自己的责任。这种假设的成立，通常可以让编译器对代码作出更多优化，同时也便于做更多的编译期检查和静态程序分析。

有时候也可能存在对于未定义行为本身的限制性要求。例如，在CPU的指令集说明中可能将某些形式的指令定为未定义，但如果该CPU支持内存保护，说明中很可能会还会包含一条兜底的规则，要求任何用户态的指令都不会让操作系统的安全性受损；这样一来，在执行未定义行为的指令时，就允许CPU破坏用户寄存器，但不允许发生诸如切换到监控模式的操作。

和未指定行为（英语：）（unspecified behavior）不同，未定义行为强调基于不可移植或错误的程序构造，或使用错误的数据。一个符合标准的实现可以在假定未定义行为永远不发生（除了显式使用不严格遵守标准的扩展）的基础上进行优化，可能导致原本存在未定义行为（例如有符号数溢出）的程序经过优化后显示出更加明显的错误（例如死循环）。因此，这种未定义行为一般应被视为bug。

好处
--

如果某一操作在文档中被定为未定义行为，编译器就可以假设该操作在符合标准的程序中永远不会发生。这样，编译器就可以得到更多的信息，获得更多优化程序的机会。

例如这样的C语言代码：

```
int foo(unsigned char x)
{
 int value = 2147483600; /\* 假设 int 是 32 位 \*/
 value += x;
 if (value < 2147483600)
 bar();
 return value;
}

```

因为 `x` 是 `unsigned char` 不可能为负数，而C语言中有符号整数的溢出又是未定义行为，编译器就可以假设执行 `if` 语句时 `value` 不可能小于 2147483600。因为这里的 `if` 没有副作用，条件也永远不成立，所以编译器就可以直接忽略 `if` 语句和对函数 `bar` 的调用。于是，上述代码在语义上就等价于：

```
int foo(unsigned char x)
{
 int value = 2147483600;
 value += x;
 return value;
}

```

如果有符号整数的溢出有明确的「环绕」行为，那么这样的程序转化就是非法的。

代码越复杂，类似的优化就越难被人类发现。如果代码同时还有其它方面的优化，例如内联展开，就更难发现了。

让有符号整数溢出未定义还有另一个好处：存储、操作变量的值时，可以在比变量本身更大的寄存器中进行。假设源代码中变量的类型比原生寄存器的宽度要窄（比如常见的在64位机器上的int类型），那么编译器就可以在生成机器码时把这个变量当作64位有符号数，对代码的语义没有任何影响。反之，如果32位有符号整数的溢出有明确定义，那么在针对64位机器编译时，编译器就必须插入额外的逻辑确保行为符合预期，因为大多数机器码指令在溢出时行为与寄存器的宽度有关。

更重要的一点是，有符号整数溢出的行为未定义，允许在编译期检查、静态程序分析、运行期检查时捕捉这类错误的情况；如果溢出行为有明确定义，就无法进行编译期检查。

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：

```
return x/0; // 未定义行为

```

某些指针操作可能导致未定义行为：

```
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\*类型的实际参数匹配，不在此引发未定义行为\*/

```

参考资料
----

1. ↑  Lattner, Chris.. LLVM Project Blog. LLVM.org. May 13, 2011  [May 24, 2011]. （原始内容存档于2014-10-30）.
2. ↑ .  [2018-06-21]. （原始内容存档于2018-07-09）.
3. ↑  ISO/IEC (2003). *ISO/IEC 14882:2003(E): Programming Languages - C++ §2.13.4 String literals [lex.string]* para. 2
4. ↑  ISO/IEC (2003). *ISO/IEC 14882:2003(E): Programming Languages - C++ §5.6 Multiplicative operators [expr.mul]* para. 4
5. ↑  ISO/IEC (2003). *ISO/IEC 14882:2003(E): Programming Languages - C++ §5.7 Additive operators [expr.add]* para. 5

外部链接
----

* The Jargon File on "nasal demons" （页面存档备份，存于），未定义行为的一个可能后果。