在C中,一个函数怎么可能有几个声明但只有一个定义?有人可以详细说明!
答案 0 :(得分:4)
为了允许多个定义,我们必须要求定义在功能上相同 - 否则你必须有一些方法来决定运行哪个,此时你可以给它们不同的名称。
证明两个函数定义相同是一个非平凡的问题。由于函数可以在多个翻译单元中声明,因此您需要在链接阶段对它们进行比较,以证明它们是相同的。当您开始处理可能考虑到这些转换单元的其他内容的编译器优化时,这是一个问题。例如,考虑:
const char *foo() {
return "world";
}
够简单吧?现在我们编译两个文件。 a.c
仅包含foo
。 b.c
也包含此内容:
const char *bar() {
return "Hello world";
}
编译器可以选择将foo()
的“世界”点置于bar()
的“Hello world”中间。链接器必须以某种方式确定两个foo()
是相同的,即使它们指向不相同的常量数据。
考虑别名规则时会出现更大的问题。考虑:
void frob(int *p);
int foo() {
int x = 1;
x++;
frob(&x);
}
单独编译,这可能会导致汇编代码类似于:
foo:
sub %esp, 4 ; allocate stack space for x
mov dword [%esp], 2 ; set x to 2 (x++ combined with initialization)
push %esp ; push %x to the stack as frob's argument
call frob
mov %eax, dword [%esp+4] ; load the value of x into eax for the return value
add %esp, 8 ; clear the stack of frob's argument and x
ret ; return
现在让我们在范围内定义frob
来编译它:
void frob(int *p) { /* no-op */ }
现在我们有:
frob:
ret ; return
foo:
mov %eax, 2 ; return value = 2
ret ; return
链接器如何告诉两个foo
定义是相同的?
鉴于证明函数体相同的困难,C选择简单地禁止您两次定义相同的函数。 C ++对内联和模板函数使用不同的方法;它根本不检查,并假设它们是相同的。
另一方面,对于声明,已经定义了用于证明兼容性的规则,因此允许多个兼容声明没有问题。
答案 1 :(得分:3)
简短的回答是"历史原因"。
声明告诉编译器如何生成调用函数的代码。
定义导致代码实现该函数。通常,编译器生成汇编代码,该汇编代码被提供给汇编程序,汇编程序生成可以提供给链接器的目标文件。编译器,汇编器和链接器传统上由不同的人编写......它们实际上不是单个"包的组成部分。
使用多个定义,编译器将发出该函数的多个副本。然后链接器将看到多次定义的函数,而不知道您要调用哪一个。然后,链接器通常会生成类似于"乘法定义的符号xxx"的错误。定义C的人不想对链接器实现者施加任何额外的负担(比如检查多个定义是否相同),所以他们只是禁止使用该语言。
使用多个声明时,唯一的负担是C编译器。
至少,这是我对理由的怀疑。
答案 2 :(得分:1)
它可以有多个声明,但all必须相同,但是拥有多个定义没有意义。编译器将如何决定应该使用哪一个?
答案 3 :(得分:1)
声明主要用在头文件中,以便其他程序可以在编译后与程序进行交互,但是AFAIK应该坚持一个声明和一个定义。多次声明似乎是一种简单的方法,使您的程序不必要地复杂化,因为您必须确保所有声明都相同。