C未检查函数返回未定义行为

时间:2018-07-22 01:37:28

标签: c gcc initialization undefined-behavior c11

在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函数中,在不检查函数返回代码的情况下,在运行时无法定义可能的值(但这不是静态确定的)。因此,通常将其包装在一个块中,例如:

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上下文中进行解答会很有帮助。

2 个答案:

答案 0 :(得分:3)

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

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

此外,编译器可以自由地假设您仅在rand()返回偶数时才调用程序。例如,它可以优化分支到return 1;的情况,从而始终打印10。

答案 1 :(得分:1)

此代码不一定调用未定义的行为。一个普遍的神话是,读取未初始化的变量总是会调用未定义的行为。 UB仅在以下两种特殊情况下发生:

  • 读取未使用其地址的未初始化变量,或者
  • 在具有陷阱整数表示形式的奇异系统上运行,其中不确定的值可能是陷阱。

在主流2的补充平台(x86 / x64,ARM,PowerPC,几乎所有...)上,此代码将仅使用未指定的值并调用未指定的行为。这是因为变量已采用其地址。 here对此进行了详细说明。

这意味着结果不可靠,但是代码将按预期执行,而无需进行优化等等。

实际上,编译器很可能不会优化函数,但这是由于time()调用,而不是由于某些定义不当的行为引起的。