在为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
?
答案 0 :(得分:4)
允许跳过局部变量的初始化,效果将是变量未初始化。
将未初始化的变量传递给puts
是undefined behaviour,但不是约束违规,也不是语法错误。这意味着C标准不要求编译器给出错误。
但是,编译器很周到并且倾向于提供各种警告标志。在这种情况下,gcc可以警告可能使用未初始化的变量。使用-Wall
或-Wuninitialized
,您应该看到警告。您可以使用-Werror
或-Werror=uninitialized
来收到错误而不是警告。
有些人建议始终使用警告标准模式进行编译,例如: -std=c11 -pedantic -Wall -Wextra
。
关于"在MSVC中,进程以非零退出代码退出。" ,MSVC编译器仅符合C89标准,在main
结束时不返回值,返回垃圾。如果需要支持那样的古代编译器,你应该在return 0;
的末尾main
。