不同编译单元中的多个函数声明

时间:2013-05-13 15:48:07

标签: c linker

zero.c:

    int sq();

one.c:

    int sq(int i) { return i*i; }

two.c:

    int sq(int i, int j);

main.c中:

    int main() {
        printf("%d\n",sq());
        printf("%d\n",sq(2));
        printf("%d\n",sq(2,3));
    }

然后我单独编译每个文件并gcc zero.o one.o two.o main.o -o main

./main给出了

1
4
4

我有点困惑,因为它如何成功运作。当我致电sq()sq(2)sq(2,3)

时,会发生什么

4 个答案:

答案 0 :(得分:0)

如果你想知道究竟发生了什么,请让gcc输出main.o的程序集并查看。我想你会发现当你调用sq()时,参数被加载到你机器上的基本寄存器中,然后sq(int i)将在第一个寄存器上执行乘法指令。如果你传递额外的参数,它们将没有任何影响,如果你没有传递任何参数,它将只适用于先前加载到该寄存器中的任何值。

答案 1 :(得分:0)

所以,我之前根据我在帖子中读到的内容写了一个答案。这是错的。这是正确的答案。

zero.c不会生成任何代码。 two.c不生成任何代码。

main.c和one.c是实际生成代码的唯一文件。

在one.c中调用一个参数sq(int i),没有参数的函数是未定义的行为(所以“任何事情都可能发生”,包括类似于你在某些情况下的预期)。使用两个参数调用也是未定义的行为 - 再次,当你这样做时它不一定会“出错”,但你不能保证它能够工作(或做你期望的事情) - 例如它也可以返回9因为它将参数从最后一个放到第一个寄存器中。

答案 2 :(得分:0)

zero.c& two.c没有任何函数定义。它只是原型声明。因此,它不会创建任何具有函数定义的汇编代码。 (提示:使用gcc -s标志进行编译以进行验证。)

只有two.c有函数定义。因此,two.s将具有函数sq,它接受​​第一个参数(通常在堆栈或处理器的第一个寄存器上传递,如英特尔上的eax或手臂中的r0)&返回它的方格。

由于你没有在main.c中给出任何原型,编译器(.c - > .s)不会抱怨。它可能可能将其视为int sq(...),但我不确定。

因此,对于3种不同的输入: sq()sq(2)sq(2,3)将调用同一个函数,该函数在two.c中声明。

现在,sq(2)&的输出sq(2,3)很明显 - 返回第一个参数的平方。 sq()的输出将取决于sq / stack中堆栈/ eax / r0上的内容。似乎是1.提示:在gdb下运行以验证。

答案 3 :(得分:0)

根据C规范,您的代码以多种方式(可能更多)调用未定义的行为:

  • 同一范围内同一函数的两个声明使用不同的返回或参数类型/计数
  • 对于在范围内没有函数原型的函数的调用,参数的数量不等于参数的数量

由于这是未定义的行为,因此您无法预测会发生什么。结果甚至不一定必须一致。如果您没有看到此代码的编译时警告/错误,那么您需要调高编译器的警告级别。

在main.c中添加原型可能会使用此代码解决编译器的警告。但是,链接器可能仍然存在问题,因为在同一范围内有多个具有相同名称的函数,并且不清楚您希望代码使用哪个函数。