Pas*_*eli 24 c goto declaration undefined-behavior
以下代码是否构成未定义的行为,因为我在变量声明之前跳转并通过指针使用它?如果是这样,标准之间是否存在差异?
int main() {
int *p = 0;
label1:
if (p) {
printf("%d\n", *p);
return 0;
}
int i = 999;
p = &i;
goto label1;
return -1;
}
Run Code Online (Sandbox Code Playgroud)
oua*_*uah 16
程序中没有未定义的行为.
goto 语句有两个约束:
(c11,6.8.6.1p1)"goto语句中的标识符应命名位于封闭函数中某处的标签.goto语句不得从具有可变修改类型的标识符范围之外跳转到该范围内.标识符".
您没有违反,并且没有其他要求超出限制.
请注意,它在c99和c90中是相同的(在某种意义上没有额外的要求).当然在c90中,由于声明和声明的混合,该程序无效.
关于在语句i之后访问对象的生命周期goto,C表示(请参阅我的重点,以下段落中的其他复制句子对于更棘手的程序将是有趣的):
(c11,6.2.4p6)" 对于没有可变长度数组类型的对象,其生命周期从入口延伸到与之关联的块,直到该块的执行以任何方式结束. [...]如果以递归方式输入块,则每次都会创建一个新的对象实例.[...]如果为对象指定了初始化,则每次执行块时都会执行声明或复合文字. ;否则,每次达到声明时,该值就变得不确定."
这意味着,阅读i时仍然活着*p; 在其生命周期之外不访问任何对象.
我会尽力回答你可能想问的问题.
您的程序的行为已明确定义.(这return -1;是有问题的;只有0,EXIT_SUCCESS并且EXIT_FAILURE被定义为从中返回的值main.但这不是你要问的.)
这个程序:
#include <stdio.h>
int main(void) {
goto LABEL;
int *p = 0;
LABEL:
if (p) {
printf("%d\n", *p);
}
}
Run Code Online (Sandbox Code Playgroud)
确实有未定义的行为.在goto将控制转移到的范围内的一个点p,但绕过它的初始化,所以p在当具有一个不确定的值if (p),执行测试.
在您的程序中,值p始终是明确定义的.在之前到达的声明goto设置p为0(空指针).该if (p)测试是假的,所以身体if不执行语句中的第一次.的goto是之后执行p已被赋予了良好限定的非空值.之后goto,if (p)测试为真,并printf执行调用.
在你的程序中,寿命两者p并i开始开放时{的main到达,期末结束时}达到或return正在执行的语句.每个的范围(即其名称可见的程序文本区域)从其声明延伸到结束}.当goto转移控制向后时,变量名称i超出范围,但该名称引用的int 对象仍然存在.该名称p在范围内(因为它是在前面声明的),并且指针对象仍然指向同一个int对象(i如果该名称可见,则其名称将是如此).
请记住,范围是指程序文本的一个区域,其中名称是可见的,而生命周期是指程序执行期间保证对象存在的时间跨度.
通常,如果对象的声明具有初始值设定项,则只要其名称可见,它就会保证它具有有效值(除非稍后为其分配了一些无效值).这可以用a goto或switch(但如果小心使用的话)绕过.
此代码没有未定义的行为.我们可以在国际标准编程语言的基本原理中找到一个很好的例子-在6.2.4 对象的存储持续时间中它说:
[...]有一个简单的经验法则:当输入块时,声明的变量是使用未指定的值创建的,但是初始化程序被评估,并且在正常过程中达到声明时将值放在变量中执行.因此,超过声明的跳跃使其未初始化,而向后跳跃将导致它不止一次初始化.如果声明未初始化变量,则将其设置为未指定的值,即使这不是第一次达到声明.
变量的范围从其声明开始.因此,尽管变量在输入块后立即存在,但在达到其声明之前,不能通过名称引用变量.
并提供以下示例:
int j = 42;
{
int i = 0;
loop:
printf("I = %4d, ", i);
printf("J1 = %4d, ", ++j);
int j = i;
printf("J2 = %4d, ", ++j);
int k;
printf("K1 = %4d, ", k);
k = i * 10;
printf("K2 = %4d, ", k);
if (i % 2 == 0) goto skip;
int m = i * 5;
skip:
printf("M = %4d\n", m);
if (++i < 5) goto loop;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
Run Code Online (Sandbox Code Playgroud)I = 0, J1 = 43, J2 = 1, K1 = ????, K2 = 0, M = ???? I = 1, J1 = 44, J2 = 2, K1 = ????, K2 = 10, M = 5 I = 2, J1 = 45, J2 = 3, K1 = ????, K2 = 20, M = 5 I = 3, J1 = 46, J2 = 4, K1 = ????, K2 = 30, M = 15 I = 4, J1 = 47, J2 = 5, K1 = ????, K2 = 40, M = 15
它说:
其中"????"表示不确定的值(并且对不确定值的任何使用都是未定义的行为).
此示例与C99标准部分草案第5段6.2.4 的对象存储持续时间一致,其中说:
对于没有可变长度数组类型的此类对象,其生命周期从entry进入与其关联的块,直到该块的执行以任何方式结束.(输入一个封闭的块或调用一个函数暂停,但不会结束,执行当前块.)如果以递归方式输入块,则每次都会创建一个新的对象实例.对象的初始值是不确定的.如果为对象指定了初始化,则每次在执行块时达到声明时都会执行初始化; 否则,每次达到声明时,该值将变为不确定.