c语言 - 为什么声明的原型的函数类型不需要与实际函数的类型匹配?



我有一个使用函数function_1的文件e1。在我用file1编译的一个文件e2中,我将函数定义为int function_1(char a)。在我的文件1中,我有原型线void function_1(char a)。当我编译和测试时,一切正常,没有任何警告。为什么这样可以?如果两种方式都有效,为什么还要要求原型函数类型呢?

我很欣赏这些警告,但只有墨尔波墨涅甚至试图回答这个问题,尽管我对这个答案不满意。如果链接器不检查类型,为什么它要成为声明的一部分呢?

这是因为编译器对给定的单个文件进行了所有检查,并且编译结果仅包含对名称function_1的引用。链接器不检查任何类型,它只是连接名称。

C使用类型有两个目的:

  1. 用于一致性检查(防止程序员(某些)明显的错误)
  2. 用于代码生成(类型告诉编译器为变量保留多少内存,生成哪些代码来访问它们等)

所有这些都是一次完成一个编译单元。(编译单元基本上是一个。c文件和它包含的所有头文件。)

可以在不同的编译单元中用不同的类型声明相同的名称。(因为编译器一次只考虑一个单元,所以它不会捕获这个。)

但这意味着你有两段代码,它们对程序处理的实体类型有不同的想法。这可能会在运行时导致有趣和令人兴奋的bug。具体细节取决于您的平台/ABI。

例如,在x86上floatint具有相同的大小(4字节)。你可以定义一个函数

float foo(void) { return 42.0f; }
文件A中的

并声明为

int foo(void);

文件b中的

,您可能期望结果程序大部分工作,除了它会将42.0f的位模式重新解释为整数。但事实并非如此:调用约定指定int%eax寄存器中返回,而float使用%st0浮点寄存器。可能的结果是,A将把42.0f放在%st0中,但B将把%eax中的任何内容作为foo的返回值。

(这可能会以更有趣的方式出错:例如,您可以在一个文件中使用float bar[] = { ... };,而在另一个文件中使用void bar(void);。这可能会导致第二个文件将浮点数组的内容解释为机器码(在调用bar()时执行)

这就是为什么编译器需要知道形参和返回值的类型:这样它就可以为函数调用生成正确的代码(将参数放在函数期望它们的地方,并从函数放置它的地方检索返回值)。

但不幸的是,它不能交叉检查多个文件中的声明,而是依赖于程序员的正确判断。这就是为什么在头文件中声明你的"导出"(或"公共")函数(即那些不标记为static)是最佳实践,该头文件由定义函数的文件和这些函数的用户都包含。这样你就不需要手动声明你想调用的函数,如果你这样做了,任何类型不匹配都将被捕获为无效/不兼容的重新声明。

最新更新