在C中,为什么函数可能有多个声明但只有一个定义?

时间:2011-06-30 20:14:45

标签: c

在C中,一个函数怎么可能有几个声明但只有一个定义?有人可以详细说明!

4 个答案:

答案 0 :(得分:4)

为了允许多个定义,我们必须要求定义在功能上相同 - 否则你必须有一些方法来决定运行哪个,此时你可以给它们不同的名称。

证明两个函数定义相同是一个非平凡的问题。由于函数可以在多个翻译单元中声明,因此您需要在链接阶段对它们进行比较,以证明它们是相同的。当您开始处理可能考虑到这些转换单元的其他内容的编译器优化时,这是一个问题。例如,考虑:

const char *foo() {
  return "world";
}

够简单吧?现在我们编译两个文件。 a.c仅包含foob.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应该坚持一个声明和一个定义。多次声明似乎是一种简单的方法,使您的程序不必要地复杂化,因为您必须确保所有声明都相同。