详解C语⾔项⽬中.h⽂件和.c⽂件的关系
详解C 语⾔项⽬中.h⽂件和.c⽂件的关系
在编译器只认识.c(.cpp))⽂件,⽽不知道.h是何物的年代,那时的⼈们写了很多的.c(.cpp)⽂件,渐渐地,⼈们发现在很
多.c(.cpp)⽂件中的声明语句就是相同的,但他们却不得不⼀个字⼀个字地重复地将这些内容敲⼊每个.c(.cpp)⽂件。但更为恐怖的是,当其中⼀个声明有变更时,就需要检查所有的.c(.cpp)⽂件。
于是⼈们将重复的部分提取出来,放在⼀个新⽂件⾥,然后在需要的.c(.cpp)⽂件中敲⼊#include XXXX这样的语句。这样即使某个声明发⽣了变更,也再不需要到处寻与修改了。因为这个新⽂件,经常被放在.c(.cpp)⽂件的头部,所以就给它起名叫做“头⽂件”,扩展名是.h。
在我们语⾔的初学阶段,往往我们的程序只有⼀个.c的⽂件或这很少的⼏个,这时我们就很少遇到头⽂件组织这个头疼的问题,随着我们程序的增加,代码量到了⼏千⾏甚⾄⼏万⾏,⽂件数也越来越多。这时这些⽂件的组织就成了⼀个问题,其实说⽩了这些⽂件的组织问题从理论上来说是软件⼯程中的模块设计等等的问题。
c语言和c++区别头⽂件的作⽤的简短描述:
(1)通过头⽂件来调⽤库功能。在很多场合,源代码不便(或不准)向⽤户公布,只要向⽤户提供头⽂件和⼆进制的库即可。⽤户只需要按照头⽂件中的接⼝声明来调⽤库功能,⽽不必关⼼接⼝怎么实现的。编译器会从库中提取相应的代码。(2)头⽂件能加强类型安全检查。如果某个接⼝被实现或被使⽤时,其⽅式与头⽂件中的声明不⼀致,编译器就会指出错误,这⼀简单的规则能⼤⼤减轻程序员调试、改错的负担。
⽐⽅说我在aaa.h⾥定义了⼀个函数的声明,然后我在aaa.h的同⼀个⽬录下建⽴aaa.c , aaa.c⾥定义了这个函数的实现,然后是在main函数所在.c⽂件⾥#include这个aaa.h 然后我就可以使⽤这个函数了。 main在运⾏时就会到这个定义了这个函数的aaa.c⽂件。这是因为:main函数为标准C/C++的程序⼊⼝,编译器会先到该函数所在的⽂件。假定编译程序编译myproj.c(其中含main())时,发现它include了mylib.h(其中声明了函数void test()),那么此时编译器将按照事先设定的路径(Include路径列表及代码⽂件所在的路径)查与之同名的实现⽂件(扩展名为.cpp或.c,此例中为mylib.c),如果到该⽂件,并在其中到该函数(此例中为void test())的实现代码,则继续编译;如果在指定⽬录不到实现⽂件,或者在该⽂件及后续的各include⽂件中未到实现代码,则返回⼀个编译错误.其实include的过程完全可以“看成”是⼀个⽂件拼接的过程,将声明和实现分别写在头⽂件及C⽂件中,或者将⼆者同时写在头⽂件中,理论上没有本质的区别。
理论上来说C⽂件与头⽂件⾥的内容,只要是C语⾔所⽀持的,⽆论写什么都可以的,⽐如你在头⽂件
中写函数体,只要在任何⼀个C⽂件包含此头⽂件就可以将这个函数编译成⽬标⽂件的⼀部分(编译是以C⽂件为单位的,如果不在任何C⽂件中包含此头⽂件的话,这段代码就形同虚设),你可以在C⽂件中进⾏函数声明,变量声明,结构体声明,这也不成问题那为何⼀定要分成头⽂件与C⽂件呢?⼜为何⼀般都在头件中进⾏函数,变量声明,宏声明,结构体声明呢?⽽在C⽂件中去进⾏变量定义,函数实现呢??
要理解C⽂件与头⽂件有什么不同之处,⾸先需要弄明⽩编译器的⼯作过程,⼀般说来编译器会做以下⼏个过程:
1.预处理阶段
2.词法与语法分析阶段
3.编译阶段,⾸先编译成纯汇编语句,再将之汇编成跟CPU相关的⼆进制码,⽣成各个⽬标⽂件
4.连接阶段,将各个⽬标⽂件中的各段代码进⾏绝对地址定位,⽣成跟特定平台相关的可执⾏⽂件,编译器在编译时是以C⽂件为单位进⾏的,也就是说如果你的项⽬中⼀个C⽂件都没有,那么你的项⽬将⽆法编译,连接器是以⽬标⽂件为单位,它将⼀个或多个⽬标⽂件进⾏函数与变量的重定位,⽣成最终的可执⾏⽂件,在PC上的程序开发,⼀般都有⼀个main函数,这是各个编译器的约定。为了⽣成
⼀个最终的可执⾏⽂件,就需要⼀些⽬标⽂件,也就是需要C⽂件,⽽这些C⽂件中⼜需要⼀个main函数作为可执⾏程序的⼊⼝。
简单些说就是C语⾔的编译分为预处理、编译、汇编、链接(test.c test.h => test.i => test.s => test.o => test)四个⼤的阶段。c⽂件中的#include宏处理,会在预处理的阶段将c中引⽤的h⽂件的内容全部写到c⽂件中,最后⽣成.i中间⽂件,这时h ⽂件中的内容就相当于被写道c⽂件中。这也为代码的复⽤提供了渠道,很多的c⽂件可以去引⽤同⼀个h⽂件,这样这个h⽂件就会被放到多个c⽂件中被编译多次,这也是h⽂件中不能放定义只能放声明的原因,放定义时被编译多次,在程序链接的时候(系统中定义了多个int a;强符号定义)会出现错误,声明就不⼀样,声明表⽰对定义的扩展,最终都会终结到⼀个定义上,所以不会出现link时重复定义的错误。
编程中我们在h⽂件中肯定都⽤过⼀下的格式
#ifndef XXX_H
#define XXX_H
//……
#endif
呵呵,那他到底有什么⽤呢,在h⽂件互相引⽤时,消除重复定义。当然宏定义是在预处理阶段发挥作⽤的,编译⽅后的过程是没有宏的影⼦的。
A.h
int a();
B.h
#include "A.h"
C.h
#include "A.h"
D.h
#include "A.h"
#include "B.h"
上⾯的D.h⽂件中就会重复出现两个int a();的声明阿,这样就有点重复了,这时条件编译宏就派上了⽤场A.h
#ifndef A_H
#define A_H
int a();
#endif
这样就不会重复定义了。
感谢阅读,希望能帮助到⼤家,谢谢⼤家对本站的⽀持!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论