我有点困惑。据我所知,如果你在C中声明一个int而没有初始化它,例如:int x;
所以它的价值是不确定的。因此,如果我们尝试使用它或应该有未定义的行为。
所以,如果我在VS2010中运行以下代码,它会使程序崩溃。
int main(){
int a;
printf("%d\n",a);
return 0;
}
现在让我们来看看下一个代码,它不提供任何警告,不会崩溃(为什么?)
void foo(int *var_handle){
// do nothing
}
int main(){
int a;
foo(&a);
printf("%d\n",a); // works, prints some big value
return 0;
}
你能解释一下这种行为吗?我们只添加了对一个什么都不做的函数的调用,但现在程序不会崩溃。
答案 0 :(得分:8)
读取未初始化变量的值会导致未定义的行为。未定义的行为意味着可以崩溃。这并不意味着 或有义务崩溃。
未初始化的变量具有未指定的值 - 它只是未知它的值是什么。所以在实践中,任何理智的实现,这种代码可能永远不会崩溃。有一个有效的内存地址支持变量,它有一些垃圾内容,printf()
读取它没有问题,将其解释为整数并打印它,就是这样。
答案 1 :(得分:3)
使用未初始化的值不会直接导致未定义的行为。
Per C 2011(n1570 draft)6.7.9 10,具有自动存储持续时间的未初始化对象具有不确定的值。根据3.19.2 1,不确定值是未指定的值或陷阱表示。类似的文字出现在C 1999中。
如果对象具有陷阱表示,则可能会出现未定义的行为。但是,如果对象具有未指定的值,则如果对象具有某个确定值,则程序必须具有行为;它只是没有指定对象具有哪个值。该程序不允许因为未指定值而崩溃。
令人惊讶的是,您在Visual Studio 2010中报告了显示崩溃的简单程序,因为我不希望int
类型在Visual Studio 2010中有任何陷阱表示。它可能是某个源文件而不是您期望编译和崩溃的内容,或者您在Visual Studio 2010中启用了特殊的调试功能,尝试跟踪未初始化的对象(但在使用foo
的第二种情况下失败)。
我建议您从头开始重复测试,将您在此问题中显示的代码粘贴到新文件中,并使用默认选项编译该新文件。
当然,Visual Studio 2010不符合C标准,甚至不符合旧的1999标准,因此不必遵守上述条款。实际上,有关Visual Studio 2010的所有内容都是与C标准相关的未定义行为。
答案 2 :(得分:1)
未定义的行为,意味着任何事情都可能发生。字面意思。行为尚未定义。
答案 3 :(得分:1)
你可以试试这个。我不知道它是否是严格未定义的行为,但是我想不出编译器实际上以未定义的方式运行并且仍然符合C标准的方法,至少在foo
处于不同的编译单元(~source文件),因为那时编译器不知道它会被允许产生未定义的行为;)。
void foo(int *var_handle){
// do something to var_handle, or maybe nothing, who knows
}
int main(){
int a[1];
foo(a);
printf("%d\n", a[0]);
return 0;
}
编辑:进一步的想法:
我很确定使用函数初始化未初始化的局部变量是可以的,方法是将非const指针指向函数的局部变量。因此,就编译器而言,仅获取局部变量的地址使得它定义具有未定义值的变量。编译器无法知道函数是否实际设置了值(函数可能在库中)。
但这只是解释了为什么它可以避免崩溃。它仍然可以是,如果允许函数内联,并且什么都不做,则允许优化器移除调用,然后删除未初始化的局部变量的地址,从而使其仍处于“未定义的行为”状态。您可以通过启动优化并从汇编输出验证来测试编译器,调用foo
内联(不生成代码),然后查看printf是否崩溃。
答案 4 :(得分:0)
无法保证使用未初始化的变量会导致程序崩溃 - 这取决于垃圾数据恰好位于为变量分配的内存位置。
答案 5 :(得分:0)
在 x86 或 x86-64 上,过早的优化会阻止从变量加载值:
int b;
b -= b; // b = eax - b, not 0
int c;
c ^= c; // c = eax - c, not 0
// eax, whatever happens to be the value in the eax register.
// rax for 64-bit, eax for 32-bit and less using bit mask,
// ax for int is 16 bit compilers.
未定义的行为可能意味着任何事情。对于未初始化的变量,它的行为与其类型相同,但值未知且无法验证。未初始化的变量会自动采用“累加器”中的值(整数:eax、rax;浮点/simd:xmm0、ymm0、zmm0 等)并且不会从随机存取存储器(或缓存)中加载它,除非它是易失性的。考虑到非左值不使用未初始化的变量,您可以通过为其赋值来初始化该变量。