为什么C编译器没有发现这个错误?

时间:2017-05-09 03:16:43

标签: c gcc clang goto sigsegv

在为code golf比赛编写一些代码时,我注意到了一些奇怪的行为。例如:

int main(void)
{
    goto jmp;
    char *str = "Hello, World!";
jmp:
    puts(str);
}

使用GCC(和Clang和MSVC)进行编译不会产生任何警告或错误,但运行它会导致SIGSEGV。为什么编译器没有发现goto正在跳变量声明?

我决定测试这个(bug?),并重写了这个例子:

int main(void)
{
    goto jmp;
    int x;
jmp:
    putchar(x);
}

同样,编译不会产生错误。此外,执行时不会抛出任何内容,但在MSVC中,进程以非零退出代码退出。

这里发生了什么?这是我们不应该使用goto的另一个原因吗?为什么在第二个例子中没有抛出任何错误,而第一个例子中会抛出SIGSEGV

1 个答案:

答案 0 :(得分:4)

允许跳过局部变量的初始化,效果将是变量未初始化。

将未初始化的变量传递给putsundefined behaviour,但不是约束违规,也不是语法错误。这意味着C标准不要求编译器给出错误。

但是,编译器很周到并且倾向于提供各种警告标志。在这种情况下,gcc可以警告可能使用未初始化的变量。使用-Wall-Wuninitialized,您应该看到警告。您可以使用-Werror-Werror=uninitialized来收到错误而不是警告。

有些人建议始终使用警告标准模式进行编译,例如: -std=c11 -pedantic -Wall -Wextra

关于"在MSVC中,进程以非零退出代码退出。" ,MSVC编译器仅符合C89标准,在main结束时不返回值,返回垃圾。如果需要支持那样的古代编译器,你应该在return 0;的末尾main