Почему я получаю "метка может быть только частью инструкции, а объявление не является выражением", если у меня есть переменная, которая инициализируется после метки?

У меня есть следующий упрощенный код:

#include <stdio.h>
int main () 
{
    printf("Hello ");
    goto Cleanup;
Cleanup:
    char *str = "World\n";
    printf("%s\n", str);
}

Я получаю сообщение об ошибке, потому что после метки объявляется новая переменная. Если я помещаю содержимое (главным образом, инициализацию) после метки в блоке {}, компиляция завершается успешно.

Я думаю, что я понимаю причину блока в случае коммутатора, но почему он должен применяться в случае метки?

Эта ошибка возникает из компилятора gcc

Ответ 1

Стандарт языка просто не позволяет этого. Ярлыкам могут следовать только утверждения, а декларации не считаются операторами в C. Самый простой способ обойти это - вставить пустой оператор после метки, что избавит вас от отслеживания области действия, как вам нужно внутри блока.

#include <stdio.h>
int main () 
{
    printf("Hello ");
    goto Cleanup;
Cleanup: ; //This is an empty statement.
    char *str = "World\n";
    printf("%s\n", str);
}

Ответ 2

Это причуда грамматики С. Метка (Cleanup:) не может появляться сразу перед объявлением (например, char *str ...;), только перед оператором (printf(...);). В C89 это не было большим затруднением, потому что объявления могли появляться только в самом начале блока, поэтому вы всегда можете немного поместить ярлык и избежать проблемы. В C99 вы можете смешивать объявления и код, но вы все равно не можете поместить ярлык непосредственно перед объявлением.

Вы можете поместить точку с запятой сразу после двоеточия метки (как предложено Renan), чтобы сделать там пустое выражение; это то, что я сделал бы в машинный код. Кроме того, поднимите объявление в верхней части функции:

int main (void) 
{
    char *str;
    printf("Hello ");
    goto Cleanup;
Cleanup:
    str = "World\n";
    printf("%s\n", str);
    return 0;
}