为何在-std=c89选项下Clang与GCC输出结果不同?
C89下旧风格函数调用类型不匹配的未定义行为分析
问题背景
测试代码如下:
#include <stdio.h> void print_sum(a, b) { printf("%d\n", a + b); } int main(void) { print_sum(1.5, 8.5); return 0; }
在x86-64平台下使用-std=c89编译时出现差异:
- Clang(trunk)输出
9 - GCC(trunk)输出
158013177
疑惑点:认为a和b默认类型为int,传递时1.5转1、8.5转8,但GCC结果不符,需明确是GCC bug还是未定义行为。
结论:这是未定义行为,并非GCC的bug
1. C89旧风格函数的参数类型规则
在C89中,采用旧风格定义函数(仅写参数名、不声明类型)时,未指定类型的参数会默认视为int类型。所以print_sum中的a和b确实是int类型,这部分理解正确。
2. 函数调用的默认参数提升与类型不匹配
问题出在函数调用阶段:
- 由于
print_sum在main调用前没有声明函数原型(仅后续有旧风格定义),调用时会触发默认参数提升规则:float类型会提升为double;- 窄整数类型(如
char、short)会提升为int或unsigned int。
- 这里传递的实参
1.5和8.5是double类型,经过默认提升后仍然是double类型,但print_sum期望接收的是int类型参数。
当实参类型(double)与函数实际期望的参数类型(int)不匹配,且无函数原型强制转换时,这种情况属于C标准定义的未定义行为。
3. 不同编译器输出差异的原因
未定义行为意味着C标准不对结果做任何保证,编译器可自由处理:
- Clang选择将
double值截断为int(1.5→1,8.5→8,求和得9); - GCC则直接将
double的二进制表示按int的方式解析(比如读取栈上double值的低32位),因此得到了看似无意义的数值158013177。
两种处理方式都符合标准,不属于编译器bug。
修复方案
要避免这种未定义行为,可采用两种方式:
- 为函数添加原型声明,明确参数类型:
编译器会自动将void print_sum(int a, int b);double实参转换为int,行为一致; - 改用C89支持的现代函数定义风格,直接在参数列表中声明类型:
void print_sum(int a, int b) { printf("%d\n", a + b); }
内容的提问来源于stack exchange,提问作者Nan Xiao
相关产品推荐
相关产品推荐

