C unchecked函数返回未定义的行为

问题描述 投票:0回答:2

在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是否初始化都不是静态可确定的。

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

这些代码的确切语义是什么(是未定义的行为,还是其他东西,或者静态和运行时未定义行为之间是否存在差异)以及标准在何处指定此内容?如果标准没有定义,我使用的是gcc,所以gcc语境中的答案会有所帮助。

c gcc initialization undefined-behavior c11
2个回答
3
投票

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

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

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


1
投票

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

  • 读取未获取其地址的未初始化变量,或
  • 在具有普通整数的陷阱表示的异域系统上运行,其中不确定的值可能是陷阱。

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

意味着结果不可靠,但代码将按预期执行,而不会优化香蕉等。

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

© www.soinside.com 2019 - 2024. All rights reserved.