作为单片机心得的开编每一章,我们聊聊C语言的头文件(.h文档)。汇编太费力,开发慢,不聊它。
我看过的C语言教科书都在谈语法,千篇一律,好像是互相抄的,没有认真体合初学者的烦恼。众多的C语言书都集体忘记谈头文件的事情。
上课的话可以请教老师,将书本没有知识补全。自学的孩子伤不起。我就是这样一个自学的孩子。好在网上有好多热心人写博客,也能从中学到一丁点儿,更多的要自己看代码,自己写,自己明悟。这过程有点慢,所以我将人家没写的部分也补一下。
H文件也叫头文件,是C语言的一种接口。可以连接别的C文件或者程序库(LIB),连接了就可以使用,能够使用什么就写在h文件上。另外也有只写公用定义和公用宏到h文件,这就只用于统一定义而尔,并不连接什么。
如果没有听明白,那么就看看用法吧。我总结出h文件的4种用法。(我说的单片机以51为主,谈的编译器就是Keil4)
用法1:调用编译器的程序库。
编译器的程序库就在LIB就在C:\Keil_v4\C51\LIB,我们只要到INC文件夹(C:\Keil_v4\C51\INC)里同面找h文件,右击编辑,就用记事本打开,可以看到能调用的函数。例如想用Sin(),它就在math.h里面。我们不用知道对应的LIB是哪一个,它总是在LIB文件夹里就行。我们只要在C文件头项上写一行#include<Math.h> 就这样便可以在你的这个C文件里使用Sin()。
是不是很简单。我们要注意,要写成<Math.h>,这大小写不重要,写成<MATH.H>也一样行,但是不要写成"Math.h",不要用双引号。不急,后面会说到双引号的。<>表示这个h文件就是Keil的INC文件夹。如果是INC里面的文件夹内h文件,例如C:\Keil_v4\C51\INC\Atmel下面的89c51rd2.h,要写成<Atmel\89c51rd2.h>。
用法2:调用编辑器的一些定义或宏。
关于编译器的事用法1说过的,就不多说,例如在FLOAT.H,里面并没有什么函数可用,但是它定义了一些宏。如果有需要调用里面的,就跟用法1一样就行。
用法3:调用别的C文件里面的函数和变量。
这是我们最常用到的。我们从网上找到一些源代码,将它们分离出来一些C文件来使用;我们要整理我们的源代码,分门别类的归纳到不同的C文件里面去。
网上找到的代码也不能"不明不白"的使用,要先看个明白才能用呀;就像我们自己写的一样。那我就拿我们自己写的代码来说吧。例如我们写了一个驱动1602屏的代码,里面有1602初始化,又有读数据函数,又有写数据函数等等。我觉得这代码写下来太臃肿了。我们可以将所有关于1602的代码剪切到一个空白的1602.c文件里面。main.c文件里立马少了好多,整个代码都清爽了。但是main.c要怎么调用1602.c呢?
第一步,是将1602.c放到工程里面。
第二步,是写1602.h文件。方法是找出1602.c里面,可以让main.c用到的函数,(有些是1602.c内部用的函数,外部无需调用,那就排除)将找到的函数原型写到1602.h文件里面。还有就是如果参数或返回值用到了某些自定义结构体,或联合等,都要写上它们的定义,如果这些定义是在某个h文件里,则要包含这个h文件。
我建议在1602.c文件里也写上一行 #include"1602.h",作用是:(1)1602.c里面的函数能从1602.h找到自己调用的函数,例如初始化Init1602()就需在调用1602的写指令(建议没有写到xxx.h里去的内部函数都写到xxx.c文件的最前头。因为每一个函数找自己调用的函数时,都是向上找的。找到xxx.h文件则进入xxx.h文件里面找)(2)参数或返回值用到了某些自定义结构体,或联合等,这定义h文件上需要,C文件上同样需要,那么这样就可省出在C文件重新写一次的麻烦。
很多情况下会出现h文件的循环包含。这会出乱子的。我们在写h文件时都会写一个条件编译。例如math.h会有这样的结构:
#ifndef __MATH_H__
#define __MATH_H__
...math.h的真正内容
#endif
/////////////////////
那么1602.h
即可以写上
#ifndef __1602_H__
#define __1602_H__
...1602.h的真正内容
#endif
这意思是说,如果没有定义__1602_H__,那就定义__1602_H__,同时也编译1602.h的内容。下一次再碰到1602.h,因为__1602_H__已经定义过了,不会再一次编译1602.h。这就免了重复编译1602.h,那么循环包含也不会出乱子了。
第三步,将1602.h包含到main.c里面就行了。
在mai.c的顶上写一行#include"1602.h"就OK。这要说说为什么用双引号。用双引号表示这个h文件在工程文件夹里。
第四步,在工程里指定H文件的所在。
打开Keil4的“为目标xxxxx设置选项”窗口,或点击。再打开C51页,点Include Paths(意思是包含路径)最右侧的...按扭,在Folder Setup窗口里面就可以管理 "工程里全部双引号h文件" 所在的文件夹。新增删除,我就不用多说了。总之,这里必然有1602.h所在的文件夹,才能在工程里使用1602.h;否则编译时就报错说,缺少1602相关函数的原型。到这里就完成了。
其它的代码也可以这样整理。不光是代码清爽了。以后这些整理出来的代码还可以重用,用在以后的程序里。好处是写好了,测试好了,就不用再多看一眼,肯定没有Bug。只管用就好了。
用法4:调用公用定义和公用宏。
例如我喜欢用u8来代替unsigned char,(type unsigned char u8; )写程序超方便。还有很多定义,适合代码移植,那么我将写到一个h文件里。又例如,我定了一个二进制的运算宏,我程序很多地方要用到。那么我就可以写一个h文件里。使用方式跟用法3是一样的。
有些人会疑问:xxx.h必须跟对应的xxx.c同名吗?答案是:没有要求同名。只是同名的话好管理。
又有人会问:一个h文件只能对应一个c文件吗?答案是:可以对应多个的。只要这些C文件都在工程里。当然不同C文件里的函数不能重名呀。
当然,关于h文件还有更多的疑问,这里不多说,我怕写多了,理解不了。先到这里。
维京猎人 2017-8-23 |