.c文件和.h文件的关系

Chase620
• 阅读 1629

最近在做一个稍微有些复杂的项目。涉及到的函数、结构体、变量等比较多。通常,我编写c/c++项目的方式是,有一个main.c文件,该文件的main函数作为接口,调用其他函数。所有其他函数按功能,分别放在不同的.h文件中,这样的方式在编译和运行上肯定是没有什么问题的。

随着项目编写的深入,结构、逻辑以及变量的日趋复杂,总感觉上述方式的实现仍然不够清晰并且会导致一些冗余。忽然记起,在课堂上,老师提到过,.h文件一般用来存放函数声明和变量名,那么为什么我在.h文件中实现函数不会有问题呢?其他.c文件和main.c文件又有什么关系呢?这些,都使我不得不重新思考.h文件和.c文件的作用和关系。

要理清.h文件的作用,我们不妨看看.h文件的由来:

 “在编译器只认识.c(.cpp))文件,而不知道.h是何物的年代,那时的人们写了很多的.c(.cpp)文件,渐渐地,人们发现在很多.c(.cpp)文件中的声明语句就是相同的,但他们却不得不一个字一个字地重复地将这些内容敲入每个.c(.cpp)文件。但更为恐怖的是,当其中一个声明有变更时,就需要检查所有的.c(.cpp)文件。
    于是人们将重复的部分提取出来,放在一个新文件里,然后在需要的.c(.cpp)文件中敲入#include XXXX这样的语句。这样即使某个声明发生了变更,也再不需要到处寻找与修改了。因为这个新文件,经常被放在.c(.cpp)文件的头部,所以就给它起名叫做“头文件”,扩展名是.h。
    在我们语言的初学阶段,往往我们的程序只有一个.c的文件或这很少的几个,这时我们就很少遇到头文件组织这个头疼的问题,随着我们程序的增加,代码 量到了几千行甚至几万行,文件数也越来越多。这时这些文件的组织就成了一个问题,其实说白了这些文件的组织问题从理论上来说是软件工程中的模块设计等等的问题。”(引自c语言项目中.h文件和.c文件的关系

由上可以看出,.h文件最初就是用来给变量和函数提供一些全局性的声明,这些声明被其他.c文件共享,方便变量和声明的修改,使得大型代码逻辑更清晰更易于维护。因此.h文件中一般是声明,很少有代码的具体实现。

那么为什么在.h文件中实现函数也不会出错呢?在.h文件中实现函数与在.c文件中实现函数有什么区别和联系呢?普通的.C文件和包含main函数的c文件有什么区别和联系呢?

要解决上述问题,首先必须弄清编译器的工作原理。编译器的最终目的是将程序员编写的源代码转换成机器能够识别运行的二进制机器码。大体上分,可以分为4个步骤:

1.头文件的预编译,预处理

  编译器在编译源代码时,会先编译头文件,保证每个头文件只被编译一次。

  在预处理阶段,编译器将c文件中引用的头文件中的内容全部写到c文件中。

2.词法和语法分析(查错)

3.编译(汇编代码,.obj文件)

   转化为汇编码,这种文件称为目标文件。后缀为.obj。

4.链接(二进制机器码,.exe文件)

  将汇编代码转换为机器码,生成可执行文件。

更详细具体的流程可参考编译器的工作过程

在编译过程中,.h文件中的所有内容会被写到包含它的.c文件中,而所有的.c文件以一个共同的main函数作为可执行程序的入口。

因此,在.h文件中编写函数实现并不会出错,相当于所有.h的内容最后都被写到了main.c文件中。

但是为了逻辑性、易于维护性以及一些其他目的(可参考c语言中.h文件和.c文件的解析 ),一般在.h文件中写函数的声明,在.c文件中编写函数的实现。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Stella981 Stella981
3年前
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解2016年09月02日00:00:36 \牧野(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fme.csdn.net%2Fdcrmg) 阅读数:59593
Stella981 Stella981
3年前
C# Aspose.Cells导出xlsx格式Excel,打开文件报“Excel 已完成文件级验证和修复。此工作簿的某些部分可能已被修复或丢弃”
报错信息:最近打开下载的Excel,会报如下错误。(xls格式不受影响)!(https://oscimg.oschina.net/oscnet/2b6f0c8d7f97368d095d9f0c96bcb36d410.png)!(https://oscimg.oschina.net/oscnet/fe1a8000d00cec3c
Stella981 Stella981
3年前
Linux查看GPU信息和使用情况
1、Linux查看显卡信息:lspci|grepivga2、使用nvidiaGPU可以:lspci|grepinvidia!(https://oscimg.oschina.net/oscnet/36e7c7382fa9fe49068e7e5f8825bc67a17.png)前边的序号"00:0f.0"是显卡的代
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
Nginx反向代理upstream模块介绍
!(https://oscimg.oschina.net/oscnet/1e67c46e359a4d6c8f36b590a372961f.gif)!(https://oscimg.oschina.net/oscnet/819eda5e7de54c23b54b04cfc00d3206.jpg)1.Nginx反