为什么函数需要事先在 C 中声明?
6 回答
这样编译器就可以在调用函数时检测到类型错误。当然有办法解决这个问题,但这是他们选择的方式。
现代 C 语言中的函数需要预先声明,原因有两个:1) 告诉编译器特定名称是函数的名称(而不是其他名称),2) 告诉编译器的确切返回类型函数,以便编译器可以正确处理该返回。
在 C 中,可以使用或不使用原型来声明函数。原型声明为编译器提供了更多信息。它包括有关函数参数(如果有)的数量和类型的信息,从而帮助编译器为函数调用正确准备参数。
在 C 语言的 C89/90 版本中,函数不必事先声明,这导致编译器在调用点对函数做出假设。不用说,在许多情况下,这被证明是危险的。
这样一次性编译器就知道要为每个参数传递多少字节。
考虑:
f(12345);
int f(char input)
{
printf("%c",input);
}
如果没有原型,编译器将假定f
接受整数,并将sizeof(int)
字节发送到函数(通过堆栈或寄存器,具体取决于平台)。但是该函数只会查看 1 个字节,这将给出错误的结果。
因为旧的编译器速度和内存有限,所以他们希望一切都可以一次完成(只需从上到下读取文件,一切都可以理解)。
现代设计的编译器可以向上、向下查找函数,甚至在尚未声明的不同文件中。
基本上,你不会......
如果您尚未声明它,许多编译器会假设您正在调用 int Function() 签名。链接器......嗯......可能不会吃那个。
如果声明的函数的签名与调用语句不匹配,编译器会介意,但在第二步出现问题: - 为参数传递和返回值预编码进行编码
后者实际上必须为每个调用确定。正是在第二个代码生成步骤中,C编译器才真正错过了声明(这就是函数签名的原因)。然后链接器还需要将函数的符号调用转换为实际的......呃......调用。但是如果函数存在于“某处”(去调查extern修饰符),链接器将不会成为显示停止器。
所有这一切的例外是函数指针机制,您可以在其中告诉编译器和链接器预期的签名,但调用本身不是由编译器决定的,链接器也没有“硬编码”调用......检查出来。
我确信这不是唯一的原因,但是当您只知道函数的声明(例如从头文件中)时,您可以通过调用其他函数来编译文件。这是可能的,无需重新编译函数本身的定义(可能在另一个文件中)。但是为了验证函数被正确调用,编译器必须知道它的声明。然后链接器会处理剩下的事情。
这里有一个小例子
主.c:
#include "function.h"
int main(){
function();
return 0;
}
函数.h:
#ifndef FUNCTION_H
#define FUNCTION_H
void function();
#endif
函数.c:
#include "function.h"
void function(){}
我正在使用 gcc 像这样编译:
gcc function.c -c
这将产生一个目标文件function.o。现在,当我想编译我的 main 函数时,我不必再编译我的 function.c 文件,我只需要知道头文件和目标文件中的声明:
gcc main.c function.o -o test
现在,目标文件将链接到我的程序中而无需重新编译。