下面举个例子,这个例子只有3个文件
/* fun.h */
#ifndef FUN_H
#define FUN_H
void base(){};
void fun();
#endif
/* fun.c */
#include "fun.h"
void fun()
{
base();
}
/* main.c */
#include "fun.h"
int main()
{
fun();
return 0;
}
$gcc -c fun.c
$gcc -c main.c
$gcc -o main main.c fun.c
则会在链接是时候报重复定义base()的错误!
fun.o: In function `base':
fun.c:(.text+0x0): multiple definition of `base'
main.o:main.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit statu
注:开始时在对这行忽然很陌生,main.c中的fun(); 突然搞不清这行代码做了什么事了!fun的函数体是调用base()方法,虽然fun是在fun.c中实现的,但在main.c文件中用,
应该是调用main.c中的base()还是fun.c中的base()呢?其实,链接器自己都搞不清清楚了,所以报错了!
这里我突然想到一个问题:如在main中调用strlen(),但是编译的时侯,仅仅编译test.c,$gcc test.c即可!不需要编译strlen所在的.c文件吗?这个该怎么解释呢?
是不是这样的,这些标准函数已经编译成了动态链接库,程序编译后,直接链接就可以!但是,是通过什么样的方法找到对应的动态链接库文件呢?
对比自己写的函数API怎么才能像系统的strlen()一样呢?直接编译调用函数的那个文件?(待补充。欢迎赐教....)
对于上面的程序,如果这样编译
$gcc -c fun.c
$gcc -o main main.c
则报没有定义fun的错误;
/tmp/ccRYqwWl.o: In function `main':
main.c:(.text+0xc): undefined reference to `fun' //未定义引用到‘fun’,因为fun.h中的fun();仅是声明!
collect2: error: ld returned 1 exit status,
---------------------------------------------------------------------------------------
为什么重定义呢?因为#include是预处理部分,在编译之前由预处理程序在这个部分复制头文件的内容过来。所以在编译时候,main.o和fun.o文件都有base()函数的定义。那么链接程序就不知道链接那个定义好了(二义性啊)
如何解决呢,为了实现“声明和实现分开”这个目标最好就是把这个base函数的函数体移到源文件里面。如果由于某种原因真的要放在头文件中...也可以。
用static声明就可以了,静态函数的作用域是文件,而不是全局。比如,上面的例子将头文件里面的void base(){}改成static void base(){},那就OK。