文件:a.h
#ifndef _a_H
#define _a_H
int poly (int a, int b, int c, int x);
int square (int x)
{
return x*x;
}
#endif // _a_H
文件:a.c
#include "a.h"
int poly (int a, int b, int c, int x)
{
return a*square(x) + b * x +c;
}
file:main.c
#include <stdio.h>
#include "a.h"
int main()
{
int p1 = poly1 (1 ,2 , 1, 5);
int p2 = poly2 (1 ,1 , 3, 5);
printf ("p1 = %d, p2 = %d\n", p1, p2);
return 0;
}
我收到了一个错误:
/tmp/ccKKrQ7u.o:在函数'square'中:
main.c :(。text + 0x0):'square'的多重定义
/tmp/ccwJoxlY.o:a.c:(.text+0x0):首先在此定义
collect2:ld返回1退出状态
所以我将函数square的实现移动到了a.c文件,它可以工作。
有人知道为什么吗?
感谢名单
答案 0 :(得分:8)
一般来说,* .c文件被编译成* .o文件,* .o文件被链接以构建可执行文件。 * .h文件未编译,它们以文本形式包含在* .c文件中,包含#included。
因此,通过#including“a.h”两次,在两个单独的* .c文件中,您已将square()的定义放入两个单独的文件中。编译完成后,最终会得到两个square()副本,每个* .o文件中有一个副本。然后,当您链接它们时,链接器会看到这两个文件,并生成错误。
如何避免这种情况?你已经发现了这个。不要将函数定义放在* .h文件中。将它们放在* .c文件中,只将函数声明放在* .h文件中。
答案 1 :(得分:6)
不要将代码放在头文件中。这两个.c文件都包含a.h
,因此它们都会获得square
的实现,因此您的链接器会抱怨。
答案 2 :(得分:5)
这是因为您的C编译器将每个.c文件构建到对象(.o)文件中,然后链接目标文件以生成可执行文件。 .c文件的内容及其包含的所有文件称为编译单元。
您的示例有两个编译单元:
main.c
,包括stdio.h
和a.h
→汇编到main.o
a.c
,包括a.h
→编译到a.o 链接器(ld)然后尝试链接.o文件,但发现它们都定义square()
,因为它位于共享a.h
中 - 因此出错。这就是为什么,正如一些人已经指出的那样,你不应该把函数定义放在头文件中。
如果您安装了nm
实用程序(您应该拥有),则可以运行
$ nm main.o
$ nm a.o
在shell中看到两个文件中都存在square
。
(编辑:我想到的这个词实际上是翻译单元,但是在搜索它们时似乎意味着几乎相同的事情。)
答案 3 :(得分:4)
当标题中有square()
时,它包含在ac和main.c中,因此每个相应的目标文件都有自己的square()
,但是如果没有静态修饰符,则它们具有完全相同的名字。将它移动到a.c意味着它只定义一次。
如果您真的想要头文件中的函数,可以使用静态内联来定义它:
static inline int square (int x)
{
return x*x;
}
静态意味着包含.h的每个.c文件都有自己的square()版本,内联意味着代码将被内联删除,并且实际上不会发生函数调用,这可能就是你想要的这里
答案 4 :(得分:1)
您不能在头文件中编写实现(函数体),否则链接器将找到对该函数的多个引用。
通常,只在头文件中放置声明,而不是定义。
答案 5 :(得分:0)
你已经回答了自己的问题:你已经在每个包含a.h的编译文件中定义了两次方块。
为了避免这种情况,你可以使方形成为静态函数
static int square (int x)
{
return x*x;
}
并添加编译器使用的内联提示,例如: inline
或__inline
。