谈谈头文件

来源:互联网 发布:泛型 java 方法 编辑:程序博客网 时间:2024/05/16 07:23
一、头文件的定义
C语言中常常使用预处理指令#include,把另一个文件的内容复制到源文件当中,被复制的文件就是头文件,其后缀名为.h。

二、头文件的作用
1、在多文件的工程当中,头文件用于全局变量、外部函数的声明,其本身不包含程序的实现代码
例如:
extern int var;//var在某个c文件中定义extern void myFunction(void);//一般情况下,都默认该函数为外部函数,因此可以把extern省略掉

2、用typedef给现有的类型起个新名字
例如:
typedef int ElementType;typedef struct LinkedList{    ElementType value;    struct LinkedList *next;}*LinkedList;

3、宏定义
例如:
#define TRUE 1#define FALSE 0

三、头文件的书写方式
  如上所述,#include的本质是把头文件的内容拷贝一份到当下的文件当中,因此我们有必要考虑一个问题——如何避免头文件重复包含。
  相信很少有人会重复写两次#include "demo.h",但是,更常见的一种重复包含的情况是:有两个头文件first.h和second.h,其中second.h中有#include "first.h"。那么当main.c同时包含first.h和second.h的时候,实际上包含了两次first.h
  因此,我们可以通过条件编译来避免重复包含。例如:编写一个头文件demo.h
#ifndef _DEMO_H#define _DEMO_H//宏定义//typedef//全局变量声明//外部函数声明#endif

四、为什么不可以在头文件中定义变量?
1、首先搞清楚编译的过程,流程图如下:
C源程序-->预编译处理(.c)-->编译、优化程序(.s、.asm)-->汇编程序(.obj、.o、.a、.ko)-->链接程序(.exe、.elf、.axf等)
对一个工程编译的时候,每个c文件都是单独编译的并得到对应的.o文件的,紧接着通过连接器把各个.o文件连接起来,最终生成统一的可执行文件。

2、假如一个工程当中,a.c和b.c都包含了同一个头文件demo.h,而demo.h里定义了变量——int var,即使我在demo.h里加了#ifndef和#endif来避免重复包含,但是编译还是会出错,编译器会说,a.o和b.o重定义了变量var。因为编译器编译的时候是分别对a.c和b.c编译的,这里并没有重复包含,但是却会分别在两个c文件定义完全相同的变量,连接器工作的时候就会发现错误并报错。

五、为什么要把函数声明写在头文件?
用一个例子说明:现在有三个文件main.c、test.c、test.h
/*****************main.c*****************/#include <stdio.h>#include "test.h"int main(int argc, char *argv[]){    Function1();    return 0;}/*****************end of main.c*****************//*****************test.c*****************/#include "test.h"void Function1(void){    Function2();}void Function2(void){    printf("Hello world!\n");}/*****************end of test.c*****************//*****************test.h*****************/#ifndef _TEST_H#define _TEST_Hvoid Function1(void);void Function2(void);#endif/*****************end of test.h*****************/

  如上面所述,编译器会分别对main.c和test.c编译

  在编译main.c的时候,#include "test.h"把Function1()的声明拷贝到当前文件的对应位置,则Function1()的作用域从当前的声明位置一直到c文件末尾。如果去掉该#include语句,则Function1()在调用的时候还不在作用域内,因此编译器会报错。

  在编译test.c的时候,如果该c文件中只有一个函数Function2()且main.c只调用Function2()的时候,不写#include "test.h"也是可以的,因为分别编译后得到的main.o和test.o经过连接器的连接,main.o中调用Function2()的时候可以跳转到test.o的Function2()的函数体当中,这个连接过程是连接器自动搜索并实现的。(当然,这是一个不好的编程习惯)

  但是,如上面例程所示,当有两个函数Function1()和Function2()的时候,定义的先后不同,如果没有函数声明,则编译器则无法识别Function1()当中的Function2()。所以一般都要养成习惯,函数调用前都声明。

另外,对于Function2(),如果它仅供test.c内定义的函数调用的话,应该定义为static函数,并把test.h中的对应声明去掉,这样就对test.c以外的文件隐藏了它的实现细节,而且避免了不同c文件编写过程中出现的函数同名问题。

六、#include "头文件"一般是放在c文件中还是放在h文件中?
  除非h文件当中必须用到某些声明,否则尽量把#include放在c文件中,例如野火STM32开发板资料当中的摄像头例程。
  bsp_ov7725.c当中除了包含bsp_ov7725.h外,根据需要还包含了bsp_sccb.h和bsp_ili9341_lcd.h。

  而stm32f10x.h却被包含在bsp_ov7725.h中,因为在STM32例程当中,这个库常常用到。更重要的是,在bsp_ov7725.h的内容中包含了下图所示的GPIOA这些标识符,而这些标识符是在stm32f10x.h中声明的,因此stm32f10x.h必须被包含在bsp_ov7725.h中。
1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小学数学没学好上初中怎么办 50天的宝宝缺钙怎么办 5个月的宝宝缺钙怎么办 空调接电后指示灯不亮怎么办 欧普led灯坏了怎么办 led灯条芯片坏了怎么办 太阳能板只有电压没有电流怎么办 农村按空调房屋不保温怎么办 自己一个人想去足疗店不敢去怎么办 吊灯led灯坏了怎么办 办健身卡老板携款跑路了怎么办 武安丰尚健身怎么办卡 热敷后眼睛肿了怎么办 两眼视力差距大怎么办 怀孕体重长得快怎么办 怀孕初期发胖6斤怎么办 孕早期长得太快怎么办 怀孕了肚子眼脏怎么办 孕38周孩子偏小怎么办 孕初期胖的厉害怎么办 怀孕干活累着了怎么办 怀孕了上班很累怎么办 孕妇胖的太快怎么办 孕妇长得太快怎么办 眼睛一按吱吱响怎么办 孕期太胖了怎么办啊 人流后子宫复位不好怎么办 怀孕初期有盆腔积液怎么办 怀孕了有盆腔积液怎么办 多囊怀孕不想要怎么办 6个月婴儿大小眼怎么办 健身教练岁数大了以后怎么办 超变战陀玩具手柄坏了怎么办 飓风战魂三陀螺中轴坏了怎么办 怎么办晚安角和铁陀螺 白衣服染上荧光剂了怎么办 指尖陀螺不亮了怎么办 手指陀螺不转了怎么办 月经推迟私处还老是流水怎么办 苹果手机刷机后忘记id密码怎么办 锤基意外怀孕怎么办零6