C中声明的、未初始化的变量会发生什么情况?它有价值吗?
如果我用C写:C中声明的、未初始化的变量会发生什么情况?它有价值吗?,c,initialization,declaration,C,Initialization,Declaration,如果我用C写: int num; 在我给num赋值之前,num的值是否不确定?静态变量(文件范围和函数静态)初始化为零: int x; // zero int y = 0; // also zero void foo() { static int x; // also zero } 非静态变量(局部变量)是不确定的。在赋值之前读取它们会导致未定义的行为 在实践中,它们一开始往往只是有一些无意义的值——一些编译器甚至可能会输入特定的固定值,以便在调试器中查看时变得明显——但严格地说,
int num;
在我给num
赋值之前,num
的值是否不确定?静态变量(文件范围和函数静态)初始化为零:
int x; // zero
int y = 0; // also zero
void foo() {
static int x; // also zero
}
非静态变量(局部变量)是不确定的。在赋值之前读取它们会导致未定义的行为
在实践中,它们一开始往往只是有一些无意义的值——一些编译器甚至可能会输入特定的固定值,以便在调试器中查看时变得明显——但严格地说,编译器可以自由地执行任何操作,从崩溃到调用
至于为什么它是未定义的行为而不是简单的“未定义/任意值”,有许多CPU体系结构在其表示中为各种类型添加了额外的标志位。一个现代的例子是;当然,C标准起草者正在考虑一些较旧的体系结构
尝试使用设置了这些标志位的值可能会导致操作中的CPU异常,该操作实际上不应该失败(例如,整数加法或分配给另一个变量)。如果你不初始化一个变量,编译器可能会在设置了这些标志位的情况下拾取一些随机垃圾,这意味着触摸未初始化的变量可能是致命的。这取决于具体情况。如果该定义是全局的(在任何函数之外),则
num
将初始化为零。如果它是局部的(在函数内部),那么它的值是不确定的。从理论上讲,即使尝试读取该值也有未定义的行为——C允许不影响该值的位的可能性,但必须以特定的方式进行设置,以便您甚至可以从读取变量中获得定义的结果。基本答案是,是的,它未定义
如果您因此看到奇怪的行为,这可能取决于声明的位置。如果在堆栈上的函数中,那么每次调用该函数时,内容都很可能不同。如果是静态范围或模块范围,则未定义但不会更改。这取决于变量的存储持续时间。具有静态存储持续时间的变量始终隐式初始化为零 对于自动(本地)变量,未初始化的变量具有不确定值。不确定值,除其他外,意味着无论您在该变量中“看到”什么“值”,它不仅是不可预测的,甚至不能保证是稳定的。例如,在实践中(即忽略UB一秒钟)此代码
int num;
int a = num;
int b = num;
不保证变量a
和b
将接收相同的值。有趣的是,这不是一些迂腐的理论概念,这在实践中很容易发生,因为优化的结果
因此,一般来说,流行的答案“它是用内存中的任何垃圾初始化的”甚至一点都不正确。未初始化变量的行为与使用垃圾初始化的变量的行为不同。0如果是静态或全局的,则为不确定,如果存储类为自动的,则为不确定
C对于对象的初始值总是非常具体的。如果是全局或静态
,它们将归零。如果auto
,则该值不确定
在C89之前的编译器中就是这样,K&R和DMR的原始C报告中都有这样的规定
C89就是这种情况,请参见第6.5.7节初始化
如果对象具有自动
存储持续时间未初始化
明确地说,它的价值是
不确定的如果一个物体
静态存储持续时间不可用
明确地初始化,它是
隐式初始化,好像每个
具有算术类型的成员为
已分配0和已分配的每个成员
指针类型被分配为空
指针常数
C99就是这种情况,请参见第6.7.8节初始化
如果对象具有自动
存储持续时间未初始化
明确地说,它的价值是
不确定的如果一个物体
静态存储持续时间不可用
显式初始化,然后:-如果 具有指针类型,则初始化为 空指针
-如果它有算术 类型,则初始化为(正) 或无符号)零
-如果它是一个 聚合时,每个成员都被初始化 (递归地)根据这些 规则
-如果是联合,则第一个 命名成员已初始化 (递归地)根据这些 规则 至于不确定到底意味着什么,我不确定C89,C99说: 3.17.2
不确定值
未指定值或陷阱 代表 但不管标准怎么说,在现实生活中,每个堆栈页实际上都以零开始,但当您的程序查看任何
auto
存储类值时,它会看到您自己的程序上次使用这些堆栈地址时留下的内容。如果你分配了大量的auto
数组,你会看到它们最终以零开始
你可能会想,为什么会这样?另一个不同的SO答案处理了这个问题,请参见:就我所知,它主要取决于编译器,但通常情况下,编译器会预先假定该值为0。
在VC++中,我得到了垃圾值,而TC给出的值是0。 我把它打印如下
int i;
printf('%d',i);
如果存储类是静态的或全局的,则在加载过程中,BSS会将变量或内存位置(ML)初始化为0,除非最初为变量分配了某个值。在局部未初始化变量的情况下,陷阱表示被分配到内存位置。因此,如果包含重要信息的任何寄存器被编译器覆盖,程序可能会崩溃 但有些编译器可能有避免这种问题的机制 我是在与nec v850系列合作时
int i;
printf('%d',i);
#include <stdio.h>
int main() {
int i;
printf("%d\n", i);
}
gcc -O0 -std=c99 a.c
0
objdump -dr a.out
0000000000400536 <main>:
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 48 83 ec 10 sub $0x10,%rsp
40053e: 8b 45 fc mov -0x4(%rbp),%eax
400541: 89 c6 mov %eax,%esi
400543: bf e4 05 40 00 mov $0x4005e4,%edi
400548: b8 00 00 00 00 mov $0x0,%eax
40054d: e8 be fe ff ff callq 400410 <printf@plt>
400552: b8 00 00 00 00 mov $0x0,%eax
400557: c9 leaveq
400558: c3 retq
#include <assert.h>
int f() {
int i = 13;
return i;
}
int g() {
int i;
return i;
}
int main() {
f();
assert(g() == 13);
}
#include <stdio.h>
int i;
int main() {
printf("%d\n", i);
}
gcc -00 -std=c99 a.c
0000000000400536 <main>:
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 8b 05 04 0b 20 00 mov 0x200b04(%rip),%eax # 601044 <i>
400540: 89 c6 mov %eax,%esi
400542: bf e4 05 40 00 mov $0x4005e4,%edi
400547: b8 00 00 00 00 mov $0x0,%eax
40054c: e8 bf fe ff ff callq 400410 <printf@plt>
400551: b8 00 00 00 00 mov $0x0,%eax
400556: 5d pop %rbp
400557: c3 retq
400558: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40055f: 00
readelf -SW a.out
[25] .bss NOBITS 0000000000601040 001040 000008 00 WA 0 0 4
uint16_t hey(uint32_t x, uint32_t mode)
{ uint16_t q;
if (mode==1) q=2;
if (mode==3) q=4;
return q; }
uint32_t wow(uint32_t mode) {
return hey(1234567, mode);
}
void moo(int mode)
{
if (mode < 5)
launch_nukes();
hey(0, mode);
}