【问题标题】:goto statement in CC语言中的goto语句
【发布时间】:2014-04-07 09:42:09
【问题描述】:
#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 的工作原理?如您所见,它是垃圾。应该说我没有声明??

【问题讨论】:

  • 不应该打印2 i 10 0xbfbeaea8 来代替它正在打印的内容吗? :O
  • 不,因为printf()中的i与顶部设置为10的i不同,因为他已经(ab)使用块来定义另一个i与printf() 相同的块范围,但那个 i 尚未初始化。
  • @Emmet 但是,未初始化的i 也没有定义。不是吗??由于goto 跳转跳过了定义语句??而之前的i 在外部范围内,即也存在于内部块中.. 不应该是这样吗? ://
  • 不,它不是定义的,而是声明的,所以它在范围内并且可以使用。范围不是你到达某个地方的控制路径的函数,只是语法。

标签: c goto variable-declaration


【解决方案1】:

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

这是 C 和 C++ 之间细微差别的细微差别之一。 In strict C++ go to crossing variable initialization is an error,而在 C 中则不是。 GCC 也证实了这一点,当您使用 -std=c11 编译时它允许,而使用 std=c++11 它会抱怨:jump to label 'f' crosses 'int i'的初始化。

来自 C99:

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

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

来自 C++11(重点是我的):

一个程序从一个具有自动存储持续时间的变量不在范围内跳转到它在范围内的一个点是格式错误的,除非该变量具有标量类型、具有平凡默认构造函数的类类型和平凡析构函数、这些类型之一的 cv 限定版本或上述类型之一的数组,声明时没有初始化器。

【讨论】:

  • goto 跳过变量声明和赋值,并且i 不再在 goto'd 块的范围内,所以它有一个垃圾值。为了清楚起见,您可能需要编辑答案;我没有兴趣将这个澄清作为一个单独的答案,因为我主要是捎带你的。
  • 我认为声明被跳过是错误的。它不是。变量声明是编译时的事情。 goto 在运行时跳过初始化到 30,因此打印出来时它包含垃圾。
  • @computerfreaker:is 都在范围内,只是后者遮蔽了前者;但是,由于goto,作业没有机会运行。
  • @slobobaby:同意(改写答案),这就是编译成功并且 goto 跳过分配的原因。见上一页评论。
  • @legends2k 我错过了第一个 i 在块外声明。你是对的,它将始终保持在范围内。我对此不好;感谢您的澄清!
【解决方案2】:

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

0 i 10        0xbfbeaea8

2 i 134513744 0xbfbeaea4

语句 X 是如何工作的?如您所见,它是垃圾。它应该 宁可说我没有声明??

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

【讨论】:

    【解决方案3】:

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

    一旦您点击goto f; 语句,您就处于第二个 i 的范围内,此时已声明并驻留在地址 0xbfbeaea4 中,但在您跳过初始化语句时未初始化。

    这就是你变得垃圾的原因。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

        试一试:我的提示是开始删除或注释掉“goto”语句。重新编译,如果没有错误,再次运行程序时看看发生了什么变化。

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

        正如他们所说,“提出正确的问题往往解决了一半的问题”。

        【讨论】:

          【解决方案6】:

          您在此printf("2 i %d %p\n",i,&amp;i); 语句中打印的i 不是i,其值为if 语句中的10,并且当您使用goto 跳过此int i = 30; 语句时,您会打印垃圾.这个int i = 30; 是要打印的i 的实际定义,即编译器分配空间和i 的值。

          【讨论】:

            【解决方案7】:

            问题在于您的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#1 隐藏在声明 i#2 的块中。运行时控制路径是否跳转到块的中间并不重要 — i#2是将要使用的i,i#1 是隐藏的(阴影)。运行时控制流不会随身携带范围。

            【讨论】:

            • 我会将“不要无缘无故创建新的块作用域”替换为“不要隐藏变量”。
            • 我部分同意。当然“不要隐藏变量”是真正的重点(相应编辑),但我认为“不要随意创建块”也值得一说。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-02-27
            • 1970-01-01
            相关资源
            最近更新 更多