在以下代码中,为什么变量i
未分配值1
?
#include <stdio.h>
int main(void)
{
int val = 0;
switch (val) {
int i = 1; //i is defined here
case 0:
printf("value: %d\n", i);
break;
default:
printf("value: %d\n", i);
break;
}
return 0;
}
当我编译时,我收到一条警告,i
尽管int i = 1;
明确初始化但未被初始化
$ gcc -Wall test.c
warning: ‘i’ is used uninitialized in this function [-Wuninitialized]
printf("value %d\n", i);
^
如果val = 0
,则输出为0
。
如果val = 1
或其他任何内容,则输出也为0.
请向我解释为什么声明变量i
但未在开关内定义。标识符为i
的对象存在自动存储持续时间(在块内)但从未初始化。为什么呢?
答案 0 :(得分:12)
根据C标准(6.8声明和块),强调我的:
3块允许对一组声明和语句进行分组 成一个句法单位。 具有的对象的初始值设定项 自动存储持续时间,以及可变长度数组声明符 对具有块范围的普通标识符进行评估和值 存储在对象中(包括存储不确定的值 在没有初始化程序的对象中)每次声明时 按执行顺序到达,就好像它是一个声明,和 在声明者出现的顺序中的每个声明中。
和(6.8.4.2开关声明)
4切换语句导致控件跳转到,进入或超过 声明是开关体,取决于a的值 控制表达式,以及是否存在默认标签和 开关体上或开关体内的任何案例标签的值。案例或默认情况 标签只能在最近的封闭开关内访问 言。
因此,永远不会评估变量i
的初始值设定项,因为声明
switch (val) {
int i = 1; //i is defined here
//...
由于跳转到大小写标签而未按执行顺序到达,并且任何具有自动存储持续时间的变量都具有不确定的值。
另见6.8.4.2/7中的这个规范性例子:
示例在人工程序片段中
switch (expr) { int i = 4; f(i); case 0: i = 17; /* falls through into default code */ default: printf("%d\n", i); }
标识符为 i 的对象存在 自动存储持续时间(在块内)但永远不会 初始化,因此如果控制表达式具有非零值 值,对printf函数的调用将访问一个不确定的 值。同样,无法访问对函数f的调用。
答案 1 :(得分:4)
在val不为零的情况下,执行直接跳转到标签default。这意味着在块中定义的变量i
未初始化且其值不确定。
6.8.2.4开关语句
- switch语句使控制跳转到,进入或超过语句 切换主体,取决于控制表达式的值,以及是否存在 默认标签以及开关主体上或中的任何案例标签的值。一个案例或 默认标签只能在最近的封闭开关语句中访问。
醇>
答案 2 :(得分:3)
确实,i
块中switch
已声明,因此它只存在于switch
内。但是,它的初始化永远不会到达,所以当val
不为0时它保持未初始化。
有点像以下代码:
{
int i;
if (val==0) goto zerovalued;
else goto nonzerovalued;
i=1; // statement never reached
zerovalued:
i = 10;
printf("value:%d\n",i);
goto next;
nonzerovalued:
printf("value:%d\n",i);
goto next;
next:
return 0;
}
直观地说,考虑原始声明,比如向编译器询问某个位置(在调用堆栈中的调用帧上,或者在寄存器中,或者其他),并将初始化视为赋值语句。两者都是单独的步骤,您可以在C中查看初始化声明,如int i=1;
作为原始声明int i;
的语法糖,然后是初始化作业i=1;
。
(实际上,事情稍微复杂一些,例如int i= i!=i;
,而且在C ++中更复杂)
答案 3 :(得分:1)
永远不会调用i变量 int i = 1; 的初始化行,因为它不属于任何可用的情况。
答案 4 :(得分:1)
具有自动存储持续时间的变量的初始化详见C11 6.2.4p6:
- 对于没有可变长度数组类型的对象,其生命周期从entry进入与其关联的块,直到该块的执行以任何方式结束。 (输入一个封闭的块或调用一个函数暂停,但不会结束,执行当前块。)如果以递归方式输入块,则每次都会创建一个新的对象实例。对象的初始值是不确定的。 如果为对象指定了初始化,则每次在执行块时到达声明或复合文字时都会执行初始化;否则,每次达到声明时,该值就会变得不确定。
醇>
即。
中i
的生命周期
switch(a) {
int i = 2;
case 1: printf("%d",i);
break;
default: printf("Hello\n");
}
从{
到}
。除非在块的执行中达到声明int i = 2;
,否则其值为 indeterminate ,。由于声明在任何案例标签之前,因此switch
跳转到相应的案例标签 - 以及初始化时,无法达到声明。
因此i
仍未初始化。因为它确实存在,并且因为它的地址从未被采用过,所以将未初始化的值用于未定义的行为 C11 6.3.2.1p2:
- [...]如果左值指定了一个自动存储持续时间的对象,该对象可以使用寄存器存储类声明(从未使用过其地址),并且该对象未初始化(未使用初始化程序声明并且没有赋值如果它在使用前已经执行过,则行为未定义。
醇>
(请注意,标准本身在此澄清了澄清括号中的内容 - 它是使用初始化程序声明的,但初始化程序未执行)。