局部变量之前的GOTO

以下代码是否构成未定义的行为,因为我在变量声明之前跳转并通过指针使用它? 如果是这样,标准之间是否存在差异?

int main() { int *p = 0; label1: if (p) { printf("%d\n", *p); return 0; } int i = 999; p = &i; goto label1; return -1; } 

程序中没有未定义的行为。

goto语句有两个约束:

(c11,6.8.6.1p1)“goto语句中的标识符应命名位于封闭函数中某处的标签.goto语句不得从具有可变修改类型的标识符范围之外跳转到该范围内。标识符“。

您没有违反,并且没有其他要求受到限制。

请注意,它在c99和c90中是相同的(在某种意义上没有额外的要求)。 当然在c90中,由于声明和声明的混合,该程序无效。

关于在goto语句之后访问i对象的生命周期,C说(参见我的重点,下一段中的其他复制句子对于一个更棘手的程序会很有趣):

(c11,6.2.4p6)“ 对于没有可变长度数组类型的对象,它的生命周期从入口延伸到与之关联的块,直到该块的执行以任何方式结束。 […]如果以递归方式输入块,则每次都会创建一个对象的新实例。[…]如果为对象指定了初始化,则每次执行块时都会执行声明或复合文字。 ;否则,每次达到声明时,该值就变得不确定。“

这意味着,当*p被读取时, i仍然活着; 在其生命周期之外不访问任何对象。

我会尽力回答你可能想问的问题。

您的程序的行为已明确定义。 ( return -1;有问题;只有0EXIT_SUCCESSEXIT_FAILURE被定义为从main返回的值。但这不是你要问的。)

这个程序:

 #include  int main(void) { goto LABEL; int *p = 0; LABEL: if (p) { printf("%d\n", *p); } } 

确实有未定义的行为。 goto将控制权转移到p范围内的某个点,但绕过其初始化,因此当执行if (p)测试时, p具有不确定的值。

在您的程序中, p的值始终定义良好。 在goto之前到达的声明将p设置为0 (空指针)。 if (p)测试为false,因此if语句的主体不会在第一次执行。 在给出明确定义的非空值之后执行goto 。 在gotoif (p)测试为真,并执行printf调用。

在你的程序中, pi生命周期从到达main开始时开始,到达close }时结束或者执行return语句。 每个的范围 (即,其名称可见的程序文本区域)从其声明延伸到结束} 。 当goto向后传输控制时,变量名称i超出范围,但该名称引用的int 对象仍然存在。 名称p在范围内(因为它是在前面声明的),并且指针对象仍然指向同一个int对象(如果该名称可见,则其名称为i )。

请记住, 范围是指程序文本的一个区域,其中名称是可见的,而生命周期是指程序执行期间保证对象存在的时间跨度。

通常,如果对象的声明具有初始值设定项,则只要其名称可见,它就会保证它具有有效值(除非稍后为其分配了一些无效值)。 这可以通过gotoswitch绕过(但如果小心使用则不行)。

此代码没有未定义的行为。 我们可以在第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; } 

输出是:

  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标准草案第6.2.4 条对象5段的储存期限,其中说:

对于没有可变长度数组类型的此类对象,其生命周期从entry进入与其关联的块,直到该块的执行以任何方式结束。 (输入一个封闭的块或调用一个函数会暂停,但不会结束当前块的执行。)如果以递归方式输入该块,则每次都会创建一个新的对象实例。 对象的初始值是不确定的。 如果为对象指定了初始化,则每次在执行块时达到声明时都会执行初始化; 否则,每次达到声明时,该值将变为不确定。