浅谈C工程中的.c与.h文件

基于C语言的单片机、arm相关的工程开发时,C语言的模块化特点体现的非常明显。试想一下:你的一个工程中需要用到AD采样模块、液晶显示模块、串口发送模块、DA控制模块等。你肯定不会选择在一个.c文件中进行,必须是分模块的,这样才有利于团队开发,提高效率。

那么模块化设计遵循着怎样的原则呢,应该怎么写.c,.h文件呢。

1. .c和.h文件的区别

通常意义上的说法,.c是源文件,.h是头文件。通常创作者为了保护其代码,而把.c文件封装起来,不公开,而将.h文件提供出来。此时.h相当于接口,供程序员调用。

但是实际上我们自己编写.h,.c文件时会发现.c与.h文件里面的内容其实并没有什么区别。在.c中写的代码同样可以在.h文件写

2.工程中使用.c,.h文件

总的来说,.h文件有两种使用方法。

方法一:

正是由于1中蓝色部分的描述的原因。我们会发现好多工程中某个模块只有.h文件,而没有.c文件

 

以上图的DSP开发【瑞泰提供的例程为例】其工程中ICETEK-VC5509-EDU.h文件中包含了对12864液晶屏的寄存器地址定义,相关操作函数定义。并没有写相关的.c文件。然后在lcd.c文件中调用ICETEK-VC5509-EDU.h。这样的做法是可行的,为什么可行,上面1中已经讲得很清楚。

但是我们再来思考一个问题。如果这个工程中我又添加了一个ad.c。在ad.c中我们需要用到液晶显示,那么我们得调用ICETEK-VC5509-EDU.h 。如果我们这么做了,工程编译链接会通过吗?

肯定不会,会报define duplicated //重复定义的错误,为什么呢? include "ICETEK-VC5509-EDU.h"相当于把这句话替换成ICETEK-VC5509-EDU.h中的内容,而ICETEK-VC5509-EDU.h中对液晶的寄存器都进行了定义,初始化,函数都已经定义了。你在两个文件中引用ICETEK-VC5509-EDU.h,肯定会报重复定义的错误。

这种方法适用于单模块,或者某个模块不会被多个.c文件调用

 

方法二:在.c文件中进行变量,数组,函数的定义,在.h文件中进行变量,数组,函数的声明。

C语言支持的是一处定义,多处声明【这里所讲的声明指的是狭义上的声明,引用声明,即不会进行内存分配】

现在将上述的ICETEK-VC5509-EDU.h分成.c和.h文件

首先在.h文件中开头写上

#ifndef __ICETEK-VC5509-EDU_H 
#define __ICETEK-VC5509-EDU_H //防止重复定义,名字与.c相同
#endif

 

然后就要进行进一步的操作

(1)宏定义,宏定义是遇到此变量即替换

所以放在.h文件中

如:

// McBSP0  ------------------------------------------------------
#define SPCR0 (*(unsigned int *)0x018c0008)
#define PCR0 (*(unsigned int *)0x018c0024)
#define SPCR01 (*(unsigned int *)0x01900008)
#define SPCR02 (*(unsigned int *)0x0)

 

(2)函数,函数的定义和声明比较清晰

.h文件进行声明

void InitInterrupt(void);   // 初始化中断
void InitCTR();      // 初始化ICETEK-CTR
void CloseCTR();     // 关闭ICETEK-CTR上各设备

.c文件进行定义

void CloseCTR()
{
    CTRGR=0;     
    CTRLR=0; CTRLR=0x40;
    CTRLR=0x0c0;
 LCDCMD(LCDCMDTURNOFF); 
 dbClearKey=CTRCLKEY;
 LBDS=0;
}

这样就可以实现多个.c文件加.h头文件,而不会导致重复定义的问题。

 

(3)数组等变量

变量的声明和定义在单个文件时好像都不怎么区分的。

变量的声明:指明变量名,变量类型。

变量的定义:指名变量名,变量类型,分配内存空间。

eg int  a; //定义

extern int a; //声明

如果进行了赋值,则肯定是定义

extern int a=0; //定义

 

所以在.h文件中:

extern unsigned char ledbuf[8],ledx[8];
extern unsigned char dbClearKey;
extern unsigned char ledkey[10][8];
extern unsigned int music[nMusicNumber][2];

//变量声明

.c文件:

unsigned int pwm[8]={ 0x86,0x87,0x83,0x8b,0x89,0x8d,0x8c,0x8e };
//变量必须要定义
unsigned char dbClearKey; //必须要定义
unsigned  char ledbuf[8];
unsigned char ledx[8];

 

在这地方还是有个疑问的,在.h文件中如果不加extern 编译也是可以通过的,那么unsigned char ledbuf[8]也能表示声明。【.h文件是先被加载进.c文件,所以.h中unsigned  char ledbuf[8]是最先执行的。为什么表示的是声明呢】

 

关于上述疑问的说明:

当时是在dsp开发环境ccs下测试的,现在在keil 环境下编译,发现按照上述做法是无法通过的,“重复定义的错误”这也验证了当时的疑问。
通过查找资料:c语言中函数本质上是外部的,所以为了方便编程,c语言允许声明函数时省略写extern【c语言规定,如果定义函数省略extern,则隐含为外部函数】
而对于变量,默认类型是auto,是局部变量,动态存储的。所以声明时不可以省略。
至于为什么CCS环境下可以通过,这个没有进一步研究。
原文地址:https://www.cnblogs.com/stoneFang/p/6715336.html