首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Undefined behavior

C语言标准精确地规定了C语言程序的可观察行为,除了以下几类:

  • 未定义的行为 - 对程序的行为没有限制。未定义行为的例子是数组边界之外的内存访问,有符号整数溢出,空指针取消引用,在没有序列点的表达式中多次修改相同标量,通过不同类型的指针访问对象等。编译器不需要诊断未定义的行为(虽然诊断了许多简单情况),编译后的程序不需要做任何有意义的事情。
  • 未指定的行为 - 允许两个或更多行为,并且不需要实现来记录每个行为的影响。例如,评估顺序,相同的字符串文字是否不同等。每个未指定的行为都会导致一组有效结果中的一个,并且在同一程序中重复时可能会产生不同的结果。
  • 实现定义的行为 - 未指定的行为,其中每个实现记录如何进行选择。例如,一个字节中的位数,或者有符号整数右移是算术还是逻辑。
  • 特定于语言环境的行为 - 实现定义的行为取决于当前选择的语言环境。例如,对于islower除26个小写拉丁字母以外的任何字符,是否返回true。

(注意:严格符合的程序不依赖于任何未指定的,未定义的或实现定义的行为)。

编译器需要为任何违反C语法规则或语义约束的程序发布诊断消息(错误或警告),即使其行为被指定为未定义或实现定义的,或者编译器提供了允许它的语言扩展接受这样的计划。对未定义行为的诊断不是必需的。

UB和优化

由于正确的C程序没有未定义的行为,因此编译器可能会在实际具有UB的程序在启用优化的情况下编译时产生意外的结果:

例如,

签名溢出

代码语言:javascript
复制
int foo(int x) {
    return x+1 > x; // either true or UB due to signed overflow
}

可能被编译为(演示)。

代码语言:javascript
复制
foo(int):
        movl    $1, %eax
        ret

访问出界

代码语言:javascript
复制
int table[4] = {};
int exists_in_table(int v)
{
    // return true in one of the first 4 iterations or UB due to out-of-bounds access
    for (int i = 0; i <= 4; i++) {
        if (table[i] == v) return 1;
    }
    return 0;
}

可以编译为(演示)。

代码语言:javascript
复制
exists_in_table(int):
        movl    $1, %eax
        ret

未初始化的标量

代码语言:javascript
复制
bool p; // uninitialized local variable
if(p) // UB access to uninitialized scalar
    puts("p is true");
if(!p) // UB access to uninitialized scalar
    puts("p is false");

可能产生以下输出(用旧版本的gcc观察):

代码语言:javascript
复制
p is true
p is false
代码语言:javascript
复制
size_t f(int x)
{
    size_t a;
    if(x) // either x nonzero or UB
        a = 42;
    return a; 
}

可以编译为(演示)。

代码语言:javascript
复制
f(int):
        mov     eax, 42
        ret

访问传递给realloc的指针

选择clang以观察显示的输出。

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
int main(void) {
    int *p = (int*)malloc(sizeof(int));
    int *q = (int*)realloc(p, sizeof(int));
    *p = 1; // UB access to a pointer that was passed to realloc
    *q = 2;
    if (p == q) // UB access to a pointer that was passed to realloc
        printf("%d%d\n", *p, *q);
}

可能的输出:

代码语言:javascript
复制
12

无限循环无副作用

选择clang以观察显示的输出。

代码语言:javascript
复制
#include <stdio.h>
 
int fermat() {
  const int MAX = 1000;
  int a=1,b=1,c=1;
  // Endless loop with no side effects is UB
  while (1) {
    if (((a*a*a) == ((b*b*b)+(c*c*c)))) return 1;
    a++;
    if (a>MAX) { a=1; b++; }
    if (b>MAX) { b=1; c++; }
    if (c>MAX) { c=1;}
  }
  return 0;
}
 
int main(void) {
  if (fermat())
    puts("Fermat's Last Theorem has been disproved.");
  else
    puts("Fermat's Last Theorem has not been disproved.");
}

可能的输出:

代码语言:javascript
复制
Fermat's Last Theorem has been disproved.

参考

  • C11标准(ISO / IEC 9899:2011):
    • 3.4行为(p:3-4)
    • 4/2未定义的行为(p:8)
  • C99标准(ISO / IEC 9899:1999):
    • 3.4行为(p:3-4)
    • 4/2未定义的行为(p:7)
  • C89 / C90标准(ISO / IEC 9899:1990):
    • 1.6术语定义

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com