为什么 clang 和 gcc 在“-std=c89”选项下输出不同的结果?

Why clang and gcc output different results under `-std=c89` option?

提问人:Nan Xiao 提问时间:11/16/2023 更新时间:11/16/2023 访问量:65

问:

我遇到了这个测验,所以想自己验证一下:

#include <stdio.h>

void
print_sum(a, b)
{
    printf("%d\n", a + b);
}

int
main(void)
{
    print_sum(1.5, 8.5);
    return 0;
}

我用 https://godbolt.org/ 来测试一下:
(1)与选项一起使用,结果是;
(2)但是对于选项,结果是 。
根据我的理解,and的默认类型应该是,所以在传递给时应该转换为和转换为。但显然没有遵循它。
x86-64 clang (trunk)-std=c899x86-64 gcc (trunk)-std=c89158013177abint1.518.58print_sum()gcc

所以我只是想知道,这是一个错误还是上面的程序有“未定义的行为?c89gcc

C GCC Clang C89

评论

0赞 Jabberwocky 11/16/2023
提示:两个编译器都会为此程序发出警告。
2赞 Lundin 11/16/2023
这是一个语言错误。“隐式 int”的这种废话特性本来就不应该存在,摆脱它是 C99 带来的主要改进之一。

答:

3赞 Keith Thompson 11/16/2023 #1

程序具有未定义的行为。

这:

void
print_sum(a, b)

是一个“老式”的宣言。它 make 和 参数类型,但它不会将该信息传达给任何调用者。abint

此调用:

print_sum(1.5, 8.5);

由于可见声明不是函数的原型,因此会导致编译器假定被调用的函数采用两个类型的参数。参数不会从 转换为 。它们只是作为 传递,并且该函数假定它已被赋予两个参数。不同的行为可能是由于不同的调用约定(不太可能,因为 gcc 和 clang 应该兼容)或不同的优化。doubledoubleintdoubleint

解决方法是将声明更改为原型,即指定其参数类型的声明。更改此设置:

void
print_sum(a, b)

对此:

void
print_sum(int a, int b)

评论

0赞 emacs drives me nuts 11/17/2023
如果可见声明不是函数的原型,那么为什么会起作用呢?void print_sum(int a, int b) {... }
0赞 Keith Thompson 11/17/2023
@emacsdrivesmenuts 这是一个原型。我没有说可见声明不是原型,我说过那个特定的可见声明()不是原型。void print_sum(a, b)
0赞 emacs drives me nuts 11/17/2023
但仍然收到警告。-Wmissing-prototypes
0赞 Keith Thompson 11/17/2023
@emacsdrivesmenuts警告一些有效代码:“如果在没有先前原型声明的情况下定义全局函数,则发出警告。 提供原型和定义,这是有效的。该警告虽然不是必需的,但是一个合理的警告。请注意,它仅适用于全局函数。gcc -Wmissing-prototypesvoid print_sum(int a, int b) { printf("%d\n", a + b); }
0赞 Keith Thompson 11/17/2023
如果定义为(因此其名称仅在该源文件中可见),则不会出现警告。如果它是全局的,那么它应该在文件中有一个声明,在相应的文件中有一个单独的定义。在玩具示例中,警告意义不大,例如问题中的代码(尽管我仍然将函数定义为,除非它们实际上需要全局)。print_sumstatic.h.cstatic