头文件中能否进行函数的定义

通常我们使用头文件时都是在头文件中进行声明,在源文件中定义,哪我们能否在头文件中进行函数的定义
  • 我们先进行一个测试,先声明一个test.h和一个test.cpp文件,并且在test.h中定义一个函数和一个变量
    在这里插入图片描述

可以发现,程序运行没有问题,结果也正确

  • 再创建一个test.cpp文件,并且同时包含tset.h,再次运行
    在这里插入图片描述

此时程序运行出错,提示出现了重定义的错误
可能有的同学会疑惑,不是已经使用了预处理指令来防止头文件重复包含了吗

#ifndef __TEST_H__
#define __TEST_H__
//头文件中的内容
#endif
使用此预处理指令的作用

防止头文件被重复包含,假如一个源文件中同时包含了头文件a和头文件b ,而头文件b中又包含a,哪在头文件展开时就会包含两份的头文件a,这样不仅浪费时间还降低了效率,而引入预处理指令就很好的解决了这个问题。

预处理的时间与范围
编译器在执行一个程序时大该分为以下几个步骤
预处理阶段:

预处理阶段是编译前的准备工作主要进行一下几个工作:

  • 执行预处理指令
  • 将头文件展开 即#include 所包含的内容
  • 进行宏替换
  • 删除注释
  • 添加行号和标识

注意:预处理阶段是各个源文件独自进行的,每个源文件互不干扰,预处理的作用范围仅在本文件中,在预处理完成后,会生成一个.i文件,而这个.i文件就是下一阶段编译时的一个编译单元,在此之后头文件就已经没有任何的作用了

编译

编译时是以编译单元为单位基本单位进行的,而编译单元即为预处理结束后每个.cpp 文件生成的.i 文件,编译时有以下工作

  • 函数和变量声明的检查
  • 语法分析 语义分析 词法分析 符号汇总等
  • 将代码转换为汇编语言

注意:编译时仅对函数和变量的声明进行检查,而不关心函数的定义,编译是以一个个单独的文件为单元的,与预处理一样,而这些编译单元都是之前生成的.i 文件。编译完成后会生成.s文件

汇编

将编译后生成的.s文件进行汇编

  • 转成机器指令
  • 生成符号表
  • 符号地址与地址重定位

在编译时会将地址用符号替代,编译器会将其翻译成虚拟地址
而地址的重定位就是建立物理内存与虚拟地址间的映射
在经过汇编阶段后,每个.s会生成一个对应的二进制的.o 文件

链接
  • 在编译完成后,生成了目标文件,此时就需要将这些目标文件链接起来生成可执行文的.exe件。
通过以上的编译器处理程序的过程,我们可以知道在链接之前,各个文件都是相互独立的,互不干扰。

此时,我们也就知道

  • 头文件是在预处理时起作用,而它的作用范围仅在当前文件
  • 编译时对于函数的定义并不关心,只关心声明,作用范围也仅在当前文件。
  • 而链接时才会将个个文件相互关联
那么之前的问题也就有答案了

因为在头文件中定义了一个函数,在预处理时头文件展开,每个文件都有了一个该函数的定义,因为编译时是分隔的,所以到链接时,将所有文件关联在一起时,发现每个包含了该头文件的文件中有一个相同函数的声明,编译器就会报出重定义的错误。

当只有一个源文件时,因为没有别的源文件的冲突所以不会报错

总结

  • 声明是在编译时处理
  • 定义是在链接时处理
  • 链接之前所有文件是隔离的

所以在头文件中尽量不要进行函数的定义,只对其进行声明。否则如果有多个源文件链接时会报错

  • 56
    点赞
  • 119
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值