在C语言中,为什么需要在goto标签后面加上一条语句?

35

我正在编写一些C代码,在我的代码中有两个嵌套的循环。在特定条件下,我想要从内部循环中breakcontinue外部循环。我试图在外部循环的代码末尾使用标签,并在满足条件时goto该标签来实现此目的。然而,gcc会报错,说我不能在复合语句的末尾使用标签。为什么?

注1: 这不是一个switch语句,关于那个问题已经在别处回答过了。

注2: 这不是一个关于风格的问题,也不是关于我是否应该使用goto语句或条件变量的问题。

编辑: 人们要求提供一个示例,我可以举一个稍微简单的例子,检查一个数组是否是另一个数组的子数组。

    int superArray[SUPER_SIZE] = {...}, subArray[SUB_SIZE] = {...};
    int superIndex, subIndex;

    for (superIndex=0; superIndex<SUPER_SIZE-SUB_SIZE; superIndex+=1)
    {
      for (subIndex=0; subIndex<SUB_SIZE; subIndex+=1)
        if (superArray[superIndex+subIndex] != subArray[subIndex])
          goto break_then_continue;

      // code that executes if subArray is a sub array

      break_then_continue:
    }

3
你能提供一个小程序来重现你所得到的错误信息吗? - sarnold
祝你好运,小心迅猛龙。 - wim
我想我只是不够好奇。我的主要感觉是你只需要去做。 - Paul Sanders
3个回答

50
在标准中明确指出标签属于语句,因此在您的标签后面加上一个简单的分号(;),就可以绕过您遇到的问题,因为这被视为语句。甚至在6.8.3/6中有使用“空”语句的示例。

EXAMPLE 3 空语句也可以用来携带标签,放置于复合语句的闭合}之前。

while (loop1) {
  /* ... */

  while (loop2) {
    /* ... */

    if (want_out)
      goto end_loop1;

    /* ... */
  }

  /* ... */

  end_loop1: ;
}

1 在标准中,这被称为null语句


6.8.1 标记语句

Syntax
  1 labeled-statement:
      identifier : statement
      case constant-expression : statement
      default : statement

请注意,上述引文中statement是不可选的。



+1 很棒的答案。喜欢直接从标准中引用的例子。 - Caleb
感谢您的热情回复。我想了解为什么gcc会以这种方式工作,而不是如何规避它。然而,您的回复似乎表明这只是C语言编写方式的一部分。这在很大程度上是源自于C编译只是汇编代码的一个小抽象的传统。 - AntonDelprado
@AntonDelprado,实际上很简单,goto会在命令/表达式之前放置标签,如果没有这样的标签,就没有地方可以跳转。使用这个空语句是为了使这种情况成为可能。就像这样,您可以跳转到一些无害的NOP(无操作)汇编函数的等效位置。 - Tomas Pruzina
哇,这意味着switch语句只是一堆goto。我想这很明显,但我从未考虑过。任何教条主义者谴责goto并希望保持一致的人也应该哀叹使用switch。要么如此,要么满足于将goto用作superbreak - SO_fix_the_vote_sorting_bug
@jdk1.0 更糟糕的是,switch 是一个计算跳转(即目标是动态的)。 :-) - melpomene
@jdk1.0 - breakcontinue都是基于goto的主题的变体。它们与原始的goto之间的一个主要区别是,结构化的变体具有有限的跳转目标范围,而goto语句可以在函数内的任何地方(“无限”目标范围)进行跳转,但有一定的限制。 - Jonathan Leffler

6
您只需编写以下内容:

您只需要编写:

label: ;

分号是一个空语句。你需要它,因为语言就是这样定义的;即使是一个空语句,你也需要转到一个语句。
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; i < M; j++)
        {
            ...
            if (some_condition)
                goto continue_loop1;
            ...
        }
continue_loop1: ;
    }

您可以对标签上的缩进进行讨论。

还要注意的是,即使有初始化,声明也不是可以被标记的语句:“label1: int x = function(y, z);”是无效的。 - Jonathan Leffler

6

C语言规范要求:

(C99,6.8.1 带标号的语句第4段) " 任何语句都可以以前缀形式出现,它将一个标识符声明为标签名称。"

在您的情况下,您可以使用空语句:

void foo(void)
{
    goto bla;

    bla:
    ;
 }

空语句不执行任何操作。

如果有变量声明,你也可以使用复合语句(块语句):

void foo(void)
{
    goto bla;

    bla:
    {
        int x = 42;
        printf("%d\n", x);
    }
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接