C中的goto语句

时间:2014-04-07 09:42:09

标签: c goto variable-declaration

#include<stdio.h>

int main() 
{
    int i  = 10;

    printf("0 i %d %p\n",i,&i);
    if (i == 10)
        goto f;

    {
        int i = 20;
        printf("1 i %d\n",i);
    }
    {
        int i = 30;
        f:
        printf("2 i %d %p\n",i,&i); //statement X
    }

    return 0;
}

输出:

[test]$ ./a.out 

0 i 10 0xbfbeaea8

2 i 134513744 0xbfbeaea4

我很难理解语句X如何工作?当你看到输出它是垃圾。应该说我没有声明??

7 个答案:

答案 0 :(得分:9)

那是因为goto跳过了阴影变量i的初始化。

这是C和C ++之间差异的细微差别之一。 In strict C++ go to crossing variable initialization is an error,而在C中却不是。 GCC也证实了这一点,当你使用 -std = c11 进行编译时,它允许使用 std = c ++ 11 它会抱怨:跳转到标签'f'十字架初始化'int i'

来自C99:

  

goto语句不得从具有可变修改类型的标识符范围之外跳转到该标识符范围内。

VLA是可变修饰类型。允许在不包含VM类型的范围内跳转。

来自C ++ 11(强调我的):

  

从具有自动存储持续时间的变量不在范围内的点跳转到其在范围内的点的程序是不正确的,除非该变量具有标量类型,具有普通默认构造函数的类类型和一个普通的析构函数,这些类型之一的cv限定版本,或者前面类型之一的数组,声明没有初始化程序

答案 1 :(得分:1)

从输出中可以清楚地看出,“i”的地址是唯一的,因为它们是在不同的范围内声明的。

0 i 10        0xbfbeaea8

2 i 134513744 0xbfbeaea4
  

语句X如何工作?当你看到输出它是垃圾。这应该   而是说我没有宣布??

i也在语句x的本地范围内声明,但由于i语句而跳过了goto到30的初始化。因此,局部变量i包含垃圾值。

答案 2 :(得分:1)

在第一个printf语句中,您访问了地址0xbfbeaea8中的i,该地址已在语句int i = 10;中声明并初始化

一旦你点击goto f;语句,你就在第二个i的范围内,此时声明它并且位于地址0xbfbeaea4但是在跳过初始化语句时没有初始化。

这就是你弄垃圾的原因。

答案 3 :(得分:0)

当控制到达第三个块时,为编译器声明i,因此i表示一些内存地址,因此编译器会尝试再次读取它。但由于i现在已超出范围,因此您无法确定它是否包含与原始值相同的值。

答案 4 :(得分:0)

我建议理解一些复杂的代码,一个接一个地删除所有“不必要的”代码并留下遗留问题。你怎么知道什么是不必要的?最初,当你不熟悉这种语言时,你会随机删除部分代码,但很快你就会知道什么是必要的,什么不是。

尝试一下:我的提示是开始删除或注释掉“goto”语句。重新编译,如果没有错误,请查看再次运行程序时发生的更改。

另一个建议是:尝试“从头开始”重新创建问题:假设您正在开展一个绝密项目,而无法向任何人显示任何单行代码,更不用说发布了在Stack Overflow上。现在,尝试通过重写等效的源代码来复制问题,这将显示相​​同的行为。

正如他们所说,“提出正确的问题通常可以解决问题的一半”。

答案 5 :(得分:0)

您在此i语句中打印的printf("2 i %d %p\n",i,&i);不是i 10语句中if的值,而是跳过此int i = 30; {1}} goto语句打印垃圾。这个int i = 30;是将要打印的i的实际定义,即编译器分配i的空间和值。

答案 6 :(得分:0)

问题是,您的goto正在跳过第二个i的作业,该作品会隐藏(隐藏)您设置的第一个i,因此您正在打印一个未初始化的变量。

你会得到一个类似的错误答案:

#include<stdio.h>

int main() 
{
    int i  = 10;                                  /* First "i" */

    printf("0 i %d %p\n",i,&i);

    {                                             /* New block scope */
        int i;                                    /* Second "i" shadows first "i" */
        printf("2 i %d %p\n",i,&i);
    }

    return 0;
}

三个教训:不要影子变量;不要无缘无故地创建块({ ... });并打开编译器警告。

只是为了澄清:变量范围是一个基于变量声明位置的编译时概念,而不是运行时发生的事情。 i#2的声明在声明i#2的块中隐藏i#1。如果运行时控制路径跳转到块的中间并不重要 - i#2是将使用的ii#1被隐藏(阴影)。运行时控制流程不会在书包中携带范围。