2022年C语言头文件的作用 .pdf

上传人:Che****ry 文档编号:27253605 上传时间:2022-07-23 格式:PDF 页数:8 大小:102.29KB
返回 下载 相关 举报
2022年C语言头文件的作用 .pdf_第1页
第1页 / 共8页
2022年C语言头文件的作用 .pdf_第2页
第2页 / 共8页
点击查看更多>>
资源描述

《2022年C语言头文件的作用 .pdf》由会员分享,可在线阅读,更多相关《2022年C语言头文件的作用 .pdf(8页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、C 语言头文件的作用最近在工作当中遇到了一点小问题,关于 C 语言头文件的应用问题 ,主要还是关于全局变量的定义和声明问题 .学习 C 语言已经有好几年了 ,工作使用也近半年了 ,但是对于这部分的东西的确还没有深入的思考过.概念上还是比较模糊的 ,只是之前的使用大多比较简单,并没有牵涉到太复杂的工程,所以定义和声明还是比较简单而明了了的.但是最近的大工程让我在这方面吃到了一点点苦头,虽然看了别人的代码能够很快的改正,但是这些改正背后的原因却不知道.我想大多数喜欢C 语言的程序员应该是和我一样的 ,总喜欢去追究程序问题背后的底层原因,而这也恰恰是我喜欢C 语言的最根本的原因 .今天看过jande

2、rs老兄在 csdn 上的一篇文章后 ,理解的确加深了很多 ,而且还学到一些以前不怎么知道的知识 .现将文章转载过来 ,并对文章当中的一些拼写错误做了简单的纠正,同时对文字及布局做了少许修改.( 如果想看原文的 ,请参考本文底部的链接.)C 语言中的 .h 文件和我认识由来已久,其使用方法虽不十分复杂,但我却是经过了几个月的 “ 不懂” 时期,几年的 “ 一知半解 ” 时期才逐渐认识清楚他的本来面目。揪其原因,我的驽钝和好学而不求甚解固然是原因之一,但另外还有其他原因。原因一:对于较小的项目,其作用不易被充分开发,换句话说就是即使不知道他的详细使用方法,项目照样进行,程序在计算机上照样跑。原因

3、二:现在的各种 C 语言书籍都是只对C 语言的语法进行详细的不能再详细的说明,但对于整个程序的文件组织构架却只字不提,找了好几本比较著名的C 语言著作,却没有一个把 .h 文件的用法写的比较透彻的。 下面我就斗胆提笔, 来按照我对 .h的认识思路,向大家介绍一下。让我们的思绪乘着时间机器回到大学一年级。C 原来老师正在讲台上讲着我们的第一个 C 语言程序 : Hello world!文件名 First.cmain()printf(“Hello world! ”);例程-1看看上面的程序,没有 .h 文件。是的,就是没有,世界上的万物都是经历从没有到有的过程的,我们对.h 的认识,我想也需要从这

4、个步骤开始。这时确实不需要 .h 文件,因为这个程序太简单了,根本就不需要。那么如何才能需要名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 8 页 - - - - - - - - - 呢?让我们把这个程序变得稍微复杂些,请看下面这个,文件名 First.cprintStr()printf(“Hello world! ”);main()printStr();例程-2还是没有 , 那就让我们把这个程序再稍微改动一下.文件名 First.cmain()printStr();pr

5、intStr()printf(“Hello world! ”);例程-3等等,不就是改变了个顺序嘛 , 但结果确是十分不同的 . 让我们编译一下例程 -2和例程 -3, 你会发现例程 -3 是编译不过的 .这时需要我们来认识一下另一个C 语言中的概念 :作用域.我们在这里只讲述与 .h 文件相关的顶层作用域 , 顶层作用域就是从声明点延伸到源程序文本结束 , 就 printStr()这个函数来说,他没有单独的声明,只有定义,那么就从他定义的行开始,到 first.c文件结束 , 也就是说 ,在在例程 -2 的main() 函数的引用点上 ,已经是他的作用域 . 例程-3 的 main() 函数

6、的引用点上,还不是他的作用域 ,所以会编译出错 . 这种情况怎么办呢 ? 有两种方法,一个就是让我们回到例程 -2,顺序对我们来说没什么 , 谁先谁后不一样呢, 只要能编译通过 ,程序能运行 , 就让 main()文件总是放到最后吧 . 那就让我们来看另一个例程 ,让我们看看这个方法是不是在任何时候都会起作用.文件名 First.cplay2().名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 8 页 - - - - - - - - - play1(); .play1()

7、 .play2();main()play1();例程-4也许大部分都会看出来了, 这就是经常用到的一种算法, 函数嵌套 , 那么让我们看看 , play1和 play2这两个函数哪个放到前面呢?这时就需要我们来使用第二种方法,使用声明 .文件名 First.cplay1();play2();play2().play1(); .play1().play2();main()play1();例程-4经历了我的半天的唠叨 , 加上四个例程的说明 ,我们终于开始了用量变引起的质变 , 这篇文章的主题 .h 文件快要出现了。一个大型的软件项目 ,可能有几千个 ,上万个 play,而不只是 play1,pl

8、ay2这么简单 , 这样就可能有 N 个类似 play1();play2();这样的声明 , 这个时候名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 8 页 - - - - - - - - - 就需要我们想办法把这样的play1();play2();也另行管理 , 而不是把他放在.c 文件中 , 于是.h 文件出现了 .文件名 First.hplay1();play2();文件名 First.C#include“first.h”play2().play1(); .play

9、1();.play2();main()play1();例程-4各位有可能会说 ,这位 janders大虾也太罗嗦了,上面这些我也知道, 你还讲了这么半天 , 请原谅 , 如果说上面的内容80% 的人都知道的话 ,那么我保证 ,下面的内容 ,80% 的人都不完全知道 . 而且这也是我讲述一件事的一贯作风,我总是想把一个东西说明白,让那些刚刚接触C 的人也一样明白 .上面是 .h 文件的最基本的功能 , 那么.h 文件还有什么别的功能呢? 让我来描述一下我手头的一个项目吧.这个项目已经做了有10 年以上了,具体多少年我们部门的人谁都说不太准确,况且时间并不是最主要的,不再详查了。是一个通讯设备的前

10、台软件, 源文件大小共51.6M,大小共 1601 个文件 , 编译后大约 10M,其庞大可想而知, 在这里充斥着错综复杂的调用关系,如在 second.c中还有一个函数需要调用 first.c文件中的 play1 函数, 如何实现呢 ?Second.h文件play1();second.c文件名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 8 页 - - - - - - - - - *().Play();.例程 -5在 second.h文件内声明 play1 函数,怎么能

11、调用到first.c文件中的哪个play1 函数中呢 ? 是不是搞错了,没有搞错 , 这里涉及到 c 语言的另一个特性 :存储类说明符 .C 语言的存储类说明符有以下几个, 我来列表说明一下说明符用法Auto只在块内变量声明中被允许, 表示变量具有本地生存期.Extern出现在顶层或块的外部变量函数与变量声明中,表示声明的对象具有静态生存期 , 连接程序知道其名字 .Static可以放在函数与变量声明中,在函数定义时 ,只用于指定函数名 ,而不将函数导出到链接程序 ,在函数声明中 ,表示其后边会有定义声明的函数,存储类型 static. 在数据声明中 ,总是表示定义的声明不导出到连接程序.无疑

12、, 在例程 -5 中的 second.h和 first.h中,需要我们用 extern标志符来修饰 play1 函数的声明 ,这样,play1()函数就可以被导出到连接程序, 也就是实现了无论在 first.c文件中调用 ,还是在 second.c文件中调用 ,连接程序都会很聪明的按照我们的意愿 ,把他连接到 first.c文件中的 play1 函数的定义上去 , 而不必我们在 second.c文件中也要再写一个一样的play1 函数.但随之有一个小问题 , 在例程-5 中,我们并没有用 extern标志符来修饰play1 啊, 这里涉及到另一个问题 , C 语言中有默认的存储类标志符. C9

13、9 中规定, 所有顶层的默认存储类标志符都是extern. 原来如此啊 , 哈哈. 回想一下例程-4,也是好险 , 我们在无知的情况下 , 竟然也误打误撞 ,用到了 extern修饰符, 否则在 first.h中声明的 play1函数如果不被连接程序导出 ,那么我们在在play2()中调用他时 , 是找不到其实际定义位置的.那么我们如何来区分 哪个头文件中的声明在其对应的.c 文件中有定义 ,而哪个又没有呢 ?这也许不是必须的,因为无论在哪个文件中定义,聪明的连接程序都会义无返顾的帮我们找到, 并导出到连接程序 , 但我觉得他确实必要的 . 因为我们需要知道这个函数的具体内容是什么,有什么功能

14、 , 有了新需求后我也许要修改他, 我需要在短时间内能找到这个函数的定义, 那么我来介绍一下在C 语言中一个人为的规范 :名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 8 页 - - - - - - - - - 在.h 文件中声明的函数 ,如果在其对应的 .c 文件中有定义 ,那么我们在声明这个函数时 ,不使用 extern修饰符 , 如果反之 ,则必须显示使用extern修饰符 .这样,在 C 语言的 .h 文件中 ,我们会看到两种类型的函数声明. 带 extern的

15、,还不带 extern的, 简单明了 ,一个是引用外部函数, 一个是自己生命并定义的函数.最终如下 :Second.h文件Externplay1();上面洋洋洒洒写了那么多都是针对函数的,而实际上 .h 文件却不是为函数所御用的 . 打开我们项目的一个 .h 文件我们发现除了函数外 ,还有其他的东西 , 那就是全局变量 .在大型项目中,对全局变量的使用不可避免, 比如,在 first.c中需要使用一个全局变量 G_test,那么我们可以在 first.h中,定义 TPYE G_test.与对函数的使用类似 , 在 second.c中我们的开发人员发现他也需要使用这个全局变量,而且要与 firs

16、t.c中一样的那个 , 如何处理 ? 对,我们可以仿照函数中的处理方法, 在 second.h中再次声明 TPYE G_test,根据 extern的用法 ,以及 c 语言中默认的存储类型 , 在两个头文件中声明的TPYE G_test, 其实其存储类型都是extern,也就是说不必我们操心 , 连接程序会帮助我们处理一切. 但我们又如何区分全局变量哪个是定义声明,哪个是引用声明呢 ?这个比函数要复杂一些 ,一般在 C 语言中有如下几种模型来区分:、初始化语句模型顶层声明中, 存在初始化语句是,表示这个声明是定义声明,其他声明是引用声明。C 语言的所有文件之中,只能有一个定义声明。按照这个模型

17、,我们可以在first.h中定义如下 TPYE G_test=1;那么就确定在 first 中的是定义声明,在其他的所有声明都是引用声明。、省略存储类型说明在这个模型中, 所有引用声明要显示的包括存储类extern,而每个外部变量的唯一定义声明中省略存储类说明符。这个与我们对函数的处理方法类似,不再举例说明。这里还有一个需要说明,本来与本文并不十分相关,但前一段有个朋友遇到此问题,相信很多人都会遇到,那就是数组全局变量。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 8

18、页 - - - - - - - - - 他遇到的问题如下:在声明定义时,定义数组如下:int G_glob100;在另一个文件中引用声明如下:int * G_glob;在 vc 中,是可以编译通过的,这种情况大家都比较模糊并且需要注意,数组与指针类似, 但并不等于说对数组的声明起变量就是指针。上面所说的的程序在运行时发现了问题, 在引用声明的那个文件中, 使用这个指针时总是提示内存访问错误, 原来我们的连接程序并不把指针与数组等同,连接时,也不把他们当做同一个定义, 而是认为是不相关的两个定义,当然会出现错误。 正确的使用方法是在引用声明中声明如下:int G_glob100;并且最好再加上一

19、个extern ,更加明了。externint G_glob100;另外需要说明的是,在引用声明中由于不需要涉及到内存分配,可以简化如下,这样在需要对全局变量的长度进行修改时,不用把所有的引用声明也全部修改了。externint G_glob;C 语言是现今为止在底层核心编程中,使用最广泛的语言,以前是,以后也不会有太大改变, 虽然现在 java,.net等语言和工具对 c 有了一定冲击, 但我们看到在计算机最为核心的地方, 其他语言是无论如何也代替不了的,而这个领域也正是我们对计算机痴迷的程序员所向往的。好了 ,看完文章 ,对与 C 语言头文件的作用应该有了跟多的理解吧,如果这些你原本都知道

20、了,那么仅当是温习一下而已 ,如果原本不知道 ,那么恭喜你 ,现在又学到一些技巧和知识.对于全局变量的定义和声明,其实还有另外一个解决的方法,聪明的你可能早已经猜到了:), 没错 ,就是用宏定义的技巧实现 .比如 a.h 文件当中有 :#ifdefAAAint i=0;#elseint i;#endif那么 ,在 a.c 文件当中 ,有如下语句 :名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 7 页,共 8 页 - - - - - - - - - .#defineAAA#inclu

21、dea.h.而对于其他的任何包含a.h 文件的头文件或者.c 源文件 ,只需要直接包含a.h 就行了.#includea.h.这样就可以达到在a.c 文件当中定义变量一次,而在其他的文件当中声明该变量的目的.当然了 ,你完全可以根据自己的需要来决定在哪个需要包含a.h 的文件当中定义宏AAA, 但是我要说的是在同一个工程的不同的需要包含a.h 的文件当中 ,你只能定义 AAA 一次 ,否则在连接这些目标文件时会出现重复定义的错误 ,即使你的单独目标文件编译没有任何的问题.当然 ,这里说的仅仅是对全局变量的声明技巧,强烈的推介大家在头文件中使用宏定义实现对整个头文件的防止重复包含 ,当然了 ,这

22、个技巧大多数的c 语言程序员都懂 .#ifndefXXX#defineXXX#endif这样做会让你的程序更加稳健,很大程度上减少了不必要的麻烦.最后给出一点点全局变量使用需要注意的问题,这也仅仅是个建议,或者说一种编程习惯;)1)所有全局变量全部以g_ 开头 ,并且尽可能声明成static类型.2)尽量杜绝跨文件访问全局变量.如果的确需要在多个文件内访问同一变量,应该由该变量定义所在文件内提供 GET/PUT函数实现 .3)全局变量必须要有一个初始值,全局变量尽量放在一个专门的函数内初始化.4)如调用的函数少于三个,请考虑改为局部变量实现.名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 8 页,共 8 页 - - - - - - - - -

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 高考资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁