为了允许多个定义,我们必须要求定义在功能上相同 - 否则您必须有某种方式来决定运行哪个,此时您可能会给它们不同的名称。
证明两个函数定义相同是一个非平凡的问题。由于函数可以在多个翻译单元中声明,因此您需要在链接阶段对它们进行比较,以证明它们是相同的。当您开始处理可能考虑到这些转换单元的其他内容的编译器优化时,这是一个问题。例如,考虑:
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++对内联和模板函数使用不同的方法;它根本不检查,并假设它们是相同的。
另一方面,对于声明,已经定义了用于证明兼容性的规则,因此允许多个兼容声明没有问题。