在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上下文中进行解答会很有帮助。
答案 0 :(得分:3)
绝大多数未定义行为不是静态确定的。大多数未定义行为的形式为“如果达到此语句,并且满足这些条件,则程序将具有未定义行为”。
就是这种情况。一次调用程序使rand()
返回奇数时,它具有未定义的行为。一次调用rand()
返回偶数时,行为是明确定义的。
此外,编译器可以自由地假设您仅在rand()
返回偶数时才调用程序。例如,它可以优化分支到return 1;
的情况,从而始终打印10。
答案 1 :(得分:1)
此代码不一定调用未定义的行为。一个普遍的神话是,读取未初始化的变量总是会调用未定义的行为。 UB仅在以下两种特殊情况下发生:
在主流2的补充平台(x86 / x64,ARM,PowerPC,几乎所有...)上,此代码将仅使用未指定的值并调用未指定的行为。这是因为变量已已采用其地址。 here对此进行了详细说明。
这意味着结果不可靠,但是代码将按预期执行,而无需进行优化等等。
实际上,编译器很可能不会优化函数,但这是由于time()
调用,而不是由于某些定义不当的行为引起的。