在教程中说
如果使用goto语句跳转到块的中间,则不会初始化该块中的自动变量。
然后在下面的代码中,如果我可以访问/声明,那么为什么它没有被初始化?
int main()
{
goto here;
{
int i=10;
here:
printf("%d\n",i);
}
return 0;
}
ps:输出是一些垃圾值。
答案 0 :(得分:9)
你的问题背后没有逻辑“如果可以访问i
,为什么......”。能够“访问i
”不是支持或反对任何事情的论据。它只是意味着printf
语句与i
的范围相同。但是,由于您跳过了初始化程序,因此该变量未初始化(正如您的教程所说)。
读取未初始化的变量是未定义的行为,因此您的程序格式不正确。
变量i
的内存已经在编译时被搁置,因为已知变量存在于内部块中。正如您可能想象的那样,内存不会动态分配。它已经存在,但由于goto
,它永远不会被设置为任何确定的。
经验法则:不要跨越初始化器。
答案 1 :(得分:2)
变量在声明它们的范围内可见(在本例中为{}
之间),而不管该范围内语句的执行顺序如何。 goto
绕过i
的初始化,这意味着在调用printf()
时它具有未定义的值。
答案 2 :(得分:1)
考虑另一个明显的情况:
int main()
{
int i; //i is declared, but not initialized
goto here;
{
i=10;//i is initialized
here: //you've skipped the initialization
printf("%d\n",i);//and got garbage
}
return 0;
}
在你的情况下:
int main()
{
goto here;
{
//printf("%d\n",i); // i does not exist here yet
int i; //from here until the end of the scope variable i exists
i=10; // i exists here and smth is written into it
here: // i exists here
printf("%d\n",i); // i exists here and it's value is accessed
}
return 0;
}
所以,int i = 5;
那真是两件事。一个是声明,并且不能通过任何东西跳过,包括goto
(很像开放新的范围也不会受到影响。你已经跳到了范围的中间,但范围已经存在)。其次是操作分配,因为它是正常操作(程序流程),所以可以跳过goto
或“中断”或“继续”,或“返回”
答案 3 :(得分:0)
C编译器将解析源文件,并“记录”任何变量初始化 到达时
printf("%d\n", i)
它将知道变量i已经存在并且他应该能够使用它,因为它在范围内。
执行空间在主函数调用之后,在执行main()中的任何代码之前,为堆栈上的i变量保留。
答案 4 :(得分:0)
因为语言标准是这样说的:
6.7.8初始化
<强>语义强>
如果没有显式初始化具有自动存储持续时间的对象,则其值是不确定的。
J.2未定义的行为
在以下情况下,行为未定义:
具有自动存储持续时间的对象的值在不确定时使用。
6.8.4.2开关语句
示例在人工程序片段中
switch (expr)
{
int i = 4;
f(i);
case 0:
i = 17;
/* falls through into default code */
default:
printf("%d\n", i);
}
标识符为i的对象存在自动存储持续时间(在块内)但从未初始化,因此如果控制表达式具有非零值,则对printf函数的调用将访问不确定的值。同样,无法访问对函数f的调用。
答案 5 :(得分:-1)
C允许您访问地址空间中的任何内容,无论它是否实际初始化。有时像崩溃或显示垃圾一样工作,有时它会打印出有用的东西,但它都是未定义的行为。方便的技巧,但是打破你的程序的好方法,所以不要认为只是得到一个结果意味着你的技巧。