14.3头文件

        在前边的章节中,多次使用过文件包含命令#include,这条指令的功能是将指定的被包含文件的全部内容插到该命令行的位置处,从而把指定文件和当前的源程序文件连成一个源文件参与编译,通常的写法有以下两种如下:

    #include <文件名>

    #include “文件名”

        使用尖括号表示预处理程序直接到系统指定的“包含文件目录”去查找,使用双引号则表示预处理程序首先在当前文件所在的文件目录中查找被包含的文件,如果没有找到才会再到系统的“包含文件目录”去查找。一般情况下的习惯是系统提供的头文件用尖括号方式,用户自己编写的头文件用双引号方式

        在前边用过很多次#include <reg52.h>,这个文件所在的位置是Keil软件安装目录的\C51\INC这个路径内。在这个文件夹内,有很多系统自带的头文件,当然也包含了<intrins.h>这个头文件。当一旦写了#include <reg52.h>这条指令后,那么相当于在当前的.c文件中,写下了以下的代码。

#ifndef __REG52_H__

#define __REG52_H__

/*  BYTE Registers  */

sfr P0    = 0x80;

sfr P1    = 0x90;

sfr P2    = 0xA0;

sfr P3    = 0xB0;

... ...

/*  BIT Registers  */

/*  PSW  */

sbit CY    = PSW^7;

sbit AC    = PSW^6;

sbit F0    = PSW^5;

sbit RS1   = PSW^4;

sbit RS0   = PSW^3;

sbit OV    = PSW^2;

sbit P     = PSW^0; //8052 only

/*  TCON  */

sbit TF1   = TCON^7;

sbit TR1   = TCON^6;

sbit TF0   = TCON^5;

sbit TR0   = TCON^4;

sbit IE1   = TCON^3;

sbit IT1   = TCON^2;

sbit IE0   = TCON^1;

sbit IT0   = TCON^0;

... ...

#endif

        之前在程序中,只要写了#include <reg52.h>这条语句,就可以随便使用P0、TCON、TMOD这些寄存器和TR0、TR1、TI、RI等这些寄存器的位,都是因为已经在这个头文件中定义或声明过了。

        Keil做了很多函数,生成了库文件,如果要使用这些函数的时候,不需要再去写这些函数的代码,而直接调用这些函数即可,只是调用之前首先要进行声明,而这些声明也放在头文件当中。比如所用的_nop_();函数,就是在<intrins.h>这个头文件中的。

        同样的,用户很多程序文件中的所要用到的函数,是在其它文件中定义的,在当前文件中要调用它们的时候,也需要提前进行外部声明。为了使程序的易维护性和可移植性提高,通常用户会自己编写所需要的头文件。用户编写的头文件中不仅仅可以进行函数的外部声明和变量的外部声明,一些宏定义也可以放在其中。

        举个例子,比如在写main.c这个文件时,配套写一个main.h文件。新建头文件的方式也很简单,和.c是类似的,首先点击新建文件的那个图标,或者点击菜单File->New,然后点击保存文件,保存的时候命名为main.h即可。为了方便编写和修改维护,在Keil编程环境中新建一个头文件组,把所有的源文件放在一个组内,把所有的头文件放在一个组内,如图14-1所示。

图14-1  工程文件分组管理

        main.h里包含了main.c所要使用的一些宏,还有对main.c内的自定义类型全局变量全局函数等需要提供给其他.c文件使用的内容,进行外部声明。比如把main.h文件写成下边这样。

/* 温度相关参数,温度数值左移4位是为与DS18B20数据格式保持一致 */

#define ACT_TEMP_ADDR         0x30   //继电器动作温度的E2存储地址

#define ACT_TEMP_MIN        (20<<4)  //继电器动作温度有效范围最小值

#define ACT_TEMP_MAX        (30<<4)  //继电器动作温度有效范围最大值

#define ACT_TEMP_DEFAULT    (25<<4)  //继电器动作温度默认值

#define ALARM_TEMP_ADDR       0x32   //高温报警温度的E2存储地址

#define ALARM_TEMP_MIN      (25<<4)  //高温报警温度有效范围最小值

#define ALARM_TEMP_MAX      (35<<4)  //高温报警温度有效范围最大值

#define ALARM_TEMP_DEFAULT  (30<<4)  //高温报警温度默认值

/* 全局数据类型定义 */

enum eStaSystem {   //系统运行状态枚举

    E_NORMAL, E_SET_ACT, E_SET_ALARM

};

void TempControl();

void KeyAction(uint8 keycode);

void ConfigTimer0(uint16 ms);

        请注意,如果对函数进行外部声明,extern是可以省略的;如果还有外部变量需要进行声明,extern是不能省略的。为了确保程序的可靠性和可移植性,尽量不使用外部变量,不同文件之间尽量采用函数传递信息。

        头文件这样编写看似没问题,实际上则不然。在程序编写过程中,经常会遇到头文件包含头文件的用法,假设a.h包含了main.h文件,b.h文件同样也包含了main.h文件,如果现在有一个c文件x.c,它既包含了a.h又包含了b.h,这样就会出现头文件main.h被x.c重复包含了,从而会发生变量函数等的重复声明,因此还得用到C语言的另一个知识点——条件编译。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐