小实验

我们一起来看两个简单的小程序:

程序 1(由 f1.c 和 f2.c 组成):

// f1.c
int main()
{
    int i = 1;
    func(i);
    return 0;
}
// f2.c
#include <stdio.h>
void func(int i)
{
    printf("%d
", i);
}

step 1-1: 编译:gcc f1.c f2.c
step 1-2: 执行:./a.out

程序 2(由 f3.c 和 f4.c 组成):

// f3.c
int main()
{
    float f = 1;
    func();
    return 0;
}
// f4.c
#include <stdio.h>

void func(float f)
{
    printf("%f
", f);
}

step 2-1: 编译:gcc f3.c f4.c
step 2-2: 执行:./a.out

问题来了

  1. 请问step 1-1 和 step 2-1 能否编译成功?
  2. 如果编译成功,step 1-2 和 step 2-2 的执行结果分别是什么?

答案:能编译成功;执行结果分别是 1 和 0.000000

为什么?

带着疑问,Google 了一番,在
Stack Overflow
上找到了这样一句话:

When C doesn’t find a declaration, it assumes this implicit declaration: int f();, which means the function can receive whatever you give it, and returns an integer.

也就是说,如果调用了一个未声明的函数f,编译器会默认这个函数的原型为 int f(); ,它可以接受任何参数。这解释了为什么两段程序能够通过编译,但是依然无法解释上面的运行结果。

在程序2中,为什么明明传递的值是 1,打印出来的却是 0.000000 ?难道编译器在传递实参的时候对其进行了隐式转换?在程序 1 中,实参的类型是 int ,在参数传递时不会发生隐式转换;但是在程序 2 中,实参类型为 float ,编译器会将其隐式转换为 double 型,float 类型的数字 1 在内存中的布局是这样的(小端序): 0x 00 00 80 3f, double 类型的数字 1 在内存中的布局是这样的: 0x 00 00 00 00 00 00 f0 3f,而当这个 double 型的实参被传递给函数 func 时,将会被解释成与形参类型一致的float型,即截断了低四位 0x 00 00 00 00 ,这样就解释了为什么打印出来的是 0.000000。

为了验证这个猜测,将 f4.c 中的 func 函数的形参改成 double 类型,然后再一次编译运行,结果果然是 1.000000 ,说明确实发生了隐式转换。

结论

  1. 调用函数前一定要声明。
  2. 编译器遇到未声明的函数时,会默认其类型为 int f(); 。
  3. 对于未声明函数的调用,编译器会对实参进行隐式转换。

参考资料

  1. Implicit function declarations in C
  2. 调用函数前未声明会出现什么情况?