`
jimmee
  • 浏览: 538751 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

C 语言项目中.h文件和.c文件的关系[转载]

 
阅读更多

在编译器只认识.c(.cpp))文件,而不知道.h是何物的年代,那时的人们写了很多的.c(.cpp)文件,渐渐地,人们发现在很多.c(.cpp)文件中的声明语句就是相同的,但他们却不得不一个字一个字地重复地将这些内容敲入每个.c(.cpp)文件。但更为恐怖的是,当其中一个声明有变更时,就需要检查所有的.c(.cpp)文件。
    于是人们将重复的部分提取出来,放在一个新文件里,然后在需要的.c(.cpp)文件中敲入#include XXXX这样的语句。这样即使某个声明发生了变更,也再不需要到处寻找与修改了。因为这个新文件,经常被放在.c(.cpp)文件的头部,所以就给它起名叫做“头文件”,扩展名是.h。
    在我们语言的初学阶段,往往我们的程序只有一个.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

这样就不会重复定义了。

分享到:
评论

相关推荐

    静态链接库与动态链接库导出函数详解(本文系转载).pdf

    使用静态链接库也很简单,在 VC 中 new 一个名为 libCall 的 Win32 Console Application 工程,并将上面生成的文件 lib.h 和 libTest.lib 文件拷贝到 libCall 的工程子目录下。libCall 工程仅包含一个 main.cpp 文件...

    静态链接库与动态链接库导出函数详解(本文系转载).docx

    例如,在 VC++6.0 中 new 一个名称为 libTest 的 static library 工程,并新建 lib.h 和 lib.cpp 两个文件,lib.h 和 lib.cpp 的源代码如下: // 文件:lib.h #ifndef LIB_H #define LIB_H extern "C" int add(int ...

    dll模板【原创转载注明】

    总结,创建DLL涉及的主要知识点包括:VS2010的项目设置、C++与汇编语言的导出函数声明、DEF文件的使用以及编译和链接过程。掌握这些技能,开发者可以灵活地创建和使用自己的DLL,提高代码复用性和效率。

    JNI完全技术手册 带完整书签

    Chap 4:用javah产生一个.h文件... 17 Chap5:jni教程(very very good) 19 Chap6: JNI传递返回值... 26 15.2.2.3 传递字符串... 28 15.2.2.4 传递整型数组... 29 15.2.2.5 传递字符串数组... 30 15.2.2.6 传递...

    MTK开发笔记个人总结版--转载

    - 整个移植过程涉及到多个文件的修改,如`SSCStringHandle.h`、`SSCStringHandle.c`和`PhoneSetup.c`,这反映了MTK平台中系统服务、语言设置和输入法集成的紧密关联性。 这篇笔记对MTK平台的开发环境配置、资源...

    转载:Introduction to Bootloader

    在文档的描述中提及的FAT BPB结构和MBR签名是理解Bootloader如何与文件系统交互和如何被计算机的固件识别的关键。而组合语言、分区表、键盘输入、VGA RAM以及跳转到C语言是编写Bootloader所必需的编程知识和技能。...

    001--OpenGL 快速入门案例解析.pdf

    最后,文档中提到了版权和许可信息,即在非商业用途下可以转载本文件内容,并需要注明出处。这体现了文档作者对知识产权保护的重视,也为读者提供了一个学习和分享知识的环境。 通过以上内容的学习,一个初学者应该...

    JAVA NATIVE

    《JAVA NATIVE VC Com 的转载连载文章》这篇文章主要探讨了如何在Java中使用Native接口与VC++编译的DLL进行交互,以及DLL的基本概念和创建方法。以下是对这些知识点的详细说明: 1. **DLL (Dynamic Link Library)**...

    STM32快速入门教程(转载)

    - 可以在`main.c`文件中编写延时函数`void delay(void)`,并通过控制GPIO的输出电平实现LED闪烁效果。 2. **中断实验**: - 通过配置中断服务例程,让STM32能够在特定条件下响应外部事件。 - 比如使用按键触发...

    Qt Creator 的安装和hello world 程序+其他程序的编写--不是一般的好

    在widget.h 文件中添加相应代码,如下,先加入头文件,再加入my2 的定义语 句,这里我们将其放到private 里,因为一般的函数都放在public 里,而变量 都放在private 里。 #ifndef WIDGET_H #define WIDGET_H #...

    流光4.71 for.zip

    接着在bbs.h中加入: #ifndef CRACKLIBPATH #define CRACKLIBPATH "/usr/lib/cracklib_dict" #endif 注意这里是字典所处的位置. 最后改动bbs的Makefile: OS_DEF = -DLINUX -DTERMIOS CC = gcc ...

    Object-C 基础语法中文版

    ### Object-C 基础语法...本教程仅提供了 Objective-C 基础语法的简要介绍,更深入的学习资料和实践案例可以在官方文档和相关书籍中找到。此外,Objective-C 社区也非常活跃,可以通过参与社区讨论来获取帮助和支持。

    Eclipse快捷键大全(转载)

    Eclipse是一款广受欢迎的开源集成开发环境(IDE),主要用于Java编程,同时也支持其他语言如C++、Python等。它的高效性和强大的功能深受开发者喜爱,其中快捷键是提高开发效率的关键工具。这篇博客“Eclipse快捷键...

    matlab面向对象编程教程-A Guide to MATLAB Object-Oriented Programming - Register, CRC 2007.rar

    MATLAB与C 、Java及其它语言的对比例程 www.mathworks.com/oop_redblack MATLAB类使用文档 www.mathworks.com/oop_doc 传感器应用代码 www.mathworks.com/dlcode1_0308 面向对象编程资料...

Global site tag (gtag.js) - Google Analytics