【问题标题】:Why don't C compilers catch this error?为什么 C 编译器不能捕捉到这个错误?
【发布时间】:2017-05-09 03:16:42
【问题描述】:

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

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

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

我决定对此(错误?)进行测试,并重写示例:

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

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

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

【问题讨论】:

  • 这是未定义的行为。您已跳过变量声明,稍后尝试使用它。甚至尝试这样做有什么意义?你期待什么样的结果?
  • 这不是编译器的工作。
  • @InternetAussie OP 提到他在打代码打高尔夫球。也许是为了this 挑战?
  • @MDXF 也许,但任何事情都不应该依赖未定义的行为。除非您知道特定编译器和实现如何处理它的详细信息,否则应该避免未定义的行为。
  • 我在 ubuntu gcc 5.4.0 上编译,带有标志 -Wall -Wextra -pedantic。没有错误,但是有一个警告`warning: 'str' is used uninitialized in this function [-Wuninitialized]`。

标签: c gcc clang goto segmentation-fault


【解决方案1】:

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

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

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

有些人建议始终在带有警告的标准模式下编译,例如-std=c11 -pedantic -Wall -Wextra.


关于“在 MSVC 中,进程以非零退出代码退出。” ,MSVC 编译器只符合 C89 标准,其中从main 的末尾脱落而不返回值,返回垃圾。如果需要支持类似的古代编译器,您应该在 main 末尾加上 return 0;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 1970-01-01
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多