首页 > 解决方案 > C 未经检查的函数返回未定义的行为

问题描述

在 c 中,这种模式相当普遍:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int init_ptr_or_return_err(int *p) {
  srand(time(NULL));
  // random to make code compile/demonstrate the question
  int error = rand() % 2;
  if (error) {
    return 1;
  }
  *p = 10;
  return 0;
}

int main() {
  int a;
  init_ptr_or_return_err(&a);
  printf("a = %d\n", a);
  return 0;
}

在上面的 main 函数中,如果不检查函数的返回码,访问 a 的值可能在运行时是未定义的(但这不是静态确定的)。因此,它通常包装在一个块中,例如:

if (init_ptr_or_return_err(&a)) {
  // error handling
} else {
  // access a
}

在这种情况下,编译器知道 a 在 else 中被初始化,因为当且仅当它设置 a 时,该函数才返回 0。因此,从技术上讲,在 else 中访问 a 是已定义的,但在 if 中访问 a 是未定义的。但是,return 0可以很容易地“从文件中返回一些固定但静态未知的值”(然后在访问 a 之前只检查该值)。因此,无论哪种情况,都无法静态确定 a 是否已初始化。

因此,在我看来,一般来说,编译器不能静态地决定这是否是未定义的行为,因此不应该例如优化它。

此类代码的确切语义是什么(它是未定义的行为,还是其他东西,或者静态和运行时未定义的行为之间是否存在差异)以及标准在哪里指定了这一点?如果这不是标准定义的,我使用的是 gcc,所以在 gcc 上下文中的答案会很有帮助。

标签: cgccinitializationundefined-behaviorc11

解决方案


绝大多数未定义的行为不是静态确定的。大多数未定义行为的形式是“如果达到此语句,并且满足这些条件,则程序具有未定义行为”。

这里就是这种情况。当一次调用程序rand()返回奇数时,它具有未定义的行为。rand()当它在返回偶数的时候被调用时,行为是明确定义的。

此外,编译器可以自由假设您只会在rand()返回偶数时调用程序。例如,它可能会优化分支到return 1;案例,因此总是打印 10。


推荐阅读