为什么头文件里面只能放声明?

现在程序开发使用各种IDE越来越多,很多程序员在日常工作中都接触不到Makefile、编译、链接这些东西了,甚至有些程序员代码写的挺好,却不知道编译、链接其实是两个步骤,只知道按照某种约定,把函数的声明放在头文件,实现放在相应的源文件里面,剩下的事情IDE都可以帮你一键搞定。

在搞清楚“为什么头文件里面只能放声明”之前,我们先想一想,头文件是用来做什么的?一个头文件是被其它头文件或源文件引用的,也就是我们经常看到的每个文件前面几行的"#include xxxx"。那么一个文件在"#include"了一个头文件之后,究竟发生了什么事儿呢?众所周知,"#include"是一个预编译指令,我们把一个引用了某个头文件的源文件预编译一下,看看究竟发生了什么~~

假设有三个文件func0.h, func1.h, func1.c,内容如下:

//func0.h
int func0() {
    return 0;
}

//func1.h
int func1();

//func1.c
#include "func0.h"
#include "func1.h"

int func1() {
    func0();
    return 1;
}

对上面的文件"func1.c"执行预编译:gcc -E func1.c,输出结果如下(注释已删除):

int func0() {
    return 0;
}

int func1();

int func1() {
    func0();
    return 1;
}

从上面的输出结果可以看出来,去除注释的内容,"#include"头文件其实就是在源文件中把头文件的内容复制了一份。所以,当一个头文件被多个文件引用的时候,这个头文件的内容也就被复制到了多个文件当中。比如头文件"func0.h"又被文件"func2.c"引用,"func2.c"预编译的结果如下:

int func0() {
    return 0;
}

int func2();

int func2() {
    func0();
    return 2;
}

从上面例子可以看出,由于"func0"的实现被放在头文件"func0.h"中,导致在"func1.c"和"func2.c"中都存在函数"func0"的实现。这时单独编译"func1.c"和"func2.c"是没有问题的,因为这时每个文件都是独立编译、还没有出现函数名的冲突。当一个文件比如"main.c"需要同时调用函数"func1"和"func2",代码如下:

//func2.h
int func2.h();


//main.c
#include "func1.h"
#include "func2.h"

int main() {
    func1();
    func2();
    return 0;
}

这时编译"main.c"也不会出错,因为"func1.h"和"func2.h"中只有函数"func1"和"func2"的声明,但是当需要链接函数"func1"和"func2"的实现时,就会报告符号重复定义错误了:

$gcc func1.o func2.o main.o -o main
func2.o: In function `func0':
func2.c:(.text+0x0): multiple definition of `func0'
func1.o:func1.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

这是因为"func1.o"和"func2.o"里面都有函数"func0"的实现,链接器不知道该使用哪一个"func0"的实现了。问题的解决办法也是显而易见的,只要把函数"func0"的实现从"func0.h"中移到"func0.c"中,然后在头文件"func0.h"中只保留函数"func0"的声明,然后在链接的时候指定"func0.o"即可:

$gcc func0.o func1.o func2.o main.o -o main

总结,从上面例子可以看出,由于"#include xxx"其实就是把某个头文件的内容在相应文件中拷贝了一份,如果头文件中放置了某个函数的实现(或全局变量的定义),当这个头文件被其它文件引用的时候,这些函数的实现或变量的定义就会出现在多个文件当中,那么当这些文件编译生成的目标文件需要被链接到一起的时候,就会出现符号重复定义的问题。

当然,如果你可以保证不需要把有重复符号的目标文件链接到一起,把函数实现或变量定义放在头文件也不会出错,但是当然也不建议这么做,这会给代码后期的维护带来诸多不便~~

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值