搜档网
当前位置:搜档网 › 11.c语言中的h文件与c文件的理解、编写及使用

11.c语言中的h文件与c文件的理解、编写及使用

在c语言编程中,我们会将要实现的应用写成.c文件:系统级的应用,我们会编写一个含有main函数的.c文件,来实现系统级的函数调用已达成我们所要的功能;具体的各个功能模块,我们习惯于写成单独的.c文件,然后在主程序main函数之前会include所需模块的.h头文件。这样的软件组织结构使程序结构清晰,便于各个模块的调试,提高了工作效率。先提出我最开始接触时的一些疑惑吧。

疑问

.c和.h文件中都有哪些内容?程序在编译连接的过程中,它们是怎么调用的?如何写c文件的头文件(.h文件)?

编译过程介绍:

首先,需要了解一下编译器的工作过程:一个程序的编译通常有包含以下几个阶段:预处理阶段、词法和语法分析阶段、编译阶段和连接阶段。当词法和语法分析无误后,编译生成目标文件(如obj文件), 之后连接器以目标文件为对象,对各段代码中的函数和变量进行绝对地址定位,生成可执行的文件(如exe文件)。

在编预处理阶段编译器开始读取C文件,当读到包含其它的都文件时,就会在所有路径中搜索相应的头文件,找到后处理.h文件中的一些声明,如果没有重复的声明,则编译器对所有.h文件处理结果会增加到当前的c文件中,生成一个新的c文件。编译阶段对这个新的c文件的函数和变量分配空间,并编译生成目标文件,每一个c文件都会生成一个目标文件。在连接阶段,连接器对各个目标文件重新定位,生成可执行文件。



c文件与h文件关系:

我们当然也可以在C文件中进行函数声明,变量声明,结构体声明,但为何一定要分成头文件与C文件呢?又为何一般都在头件中进行函数,变量声明,宏声明,结构体声明呢?而在C文件中去进行变量定义,函数实现呢?主要原因如下:



1.如果在头文件中实现一个函数体,那么如果在多个C文件中引用它,而且又同时编译多个C文件,将其生成的目标文件连接成一个可执行文件,在每个引用此头文件的C文件所生成的目标文件中,都有一份这个函数的代码,如果这段函数又没有定义成局部函数,那么在连接时,就会发现多个相同的函数,就会报错
2.如果在头文件中定义全局变量,并且将此全局变量赋初值,那么在多个引用此头文件的C文件中同样存在相同变量名的拷贝,关键是此变量被赋了初值,所以编译器就会将此变量放入DATA段,最终在连接阶段,会在DATA段中存在多个相同的变量,它无法将这些变量统一成一个变量,也就是仅为此变量分配一个空间,而不是多份空间,假定这个变量在头文件没有赋初值,编译器就会将之放入BSS段,连接器会对BSS段的

多个同名变量仅分配一个存储空间
3.如果在C文件中声明宏,结构体,函数等,那么我要在另一个C文件中引用相应的宏,结构体,就必须再做一次重复的工作,如果我改了一个C文件中的一个声明,那么又忘了改其它C文件中的声明,这不就出了大问题了,程序的逻辑就变成了你不可想象的了,如果把这些公共的东东放在一个头文件中,想用它的C文件就只需要引用一个就OK了!!!这样岂不方便,要改某个声明的时候,只需要动一下头文件就行了
4.在头文件中声明结构体,函数等,当你需要将你的代码封装成一个库,让别人来用你的代码,你又不想公布源码,那么人家如何利用你的库呢?也就是如何利用你的库中的各个函数呢??一种方法是公布源码,别人想怎么用就怎么用,另一种是提供头文件,别人从头文件中看你的函数原型,这样人家才知道如何调用你写的函数,就如同你调用printf函数一样,里面的参数是怎样的??你是怎么知道的??还不是看人家的头文件中的相关声明啊!!!当然这些东东都成了C标准,就算不看人家的头文件,你一样可以知道怎么使用.



////////////////////////////////////////////////////////////////////////

编写h文件实例说明


例如在main.c的文件中#include“uart.h”后,程序在编译的过程中,会首先将的寻找并调用uart.h文件,生成目标文件,在连接阶段连接器将由main.c和uart.c生成的两个目标文件分配地址,即明确程序入口地址,相应函数之间的调用关系,生成了实现uart模块功能的应用程序。


以下程序不用细看,我们只需要知道该c文件是一些实现具体功能的函数组成的就行。该c文件也include了一些头文件,如编译过程介绍的那样,这写文件是编译该c文件所需的。

UART的C文件

#include "system.h"
#include "altera_avalon_pio_regs.h"
#include "alt_types.h"

#include "sys/alt_stdio.h"
#include
#include
#include
#include "altera_avalon_uart_regs.h"

//UART发送一个字节子程序
void Uart_send(unsigned char data)
{
alt_u16 status;
status=IORD_ALTERA_AVALON_UART_STATUS(UART_BASE);
while(!(status&0x0040))//等待发送完成
status=IORD_ALTERA_AVALON_UART_STATUS(UART_BASE);
IOWR_ALTERA_AVALON_UART_TXDATA(UART_BASE,data);
}
//UART发送多个字节子程序
void Uart_send_n(unsigned char *ptr,unsigned char n)
{
for(;n>0;n--)
{
Uart_send(*ptr);
ptr++;
}
}
//UART接收子程序
int Uart_receive(void)
{
alt_u16 status;
int temp;
status=IORD_ALTERA_AVALON_UART_STATUS(UART_BASE);
while(!(status&0x0080))//等待发送完成
status=IORD_ALTERA_AVALON_UART_STATUS(UART_BASE);
temp=IORD_ALTERA_AVALON_UART_RXDATA(UART_BASE);
return temp;
}
//串口接收

中断服务程序
void Uart_ISR(void * context,alt_u32 id)
{
unsigned char temp;
unsigned char leddata;
temp=IORD_ALTERA_AVALON_UART_RXDATA(UART_BASE);
leddata=temp;
Uart_send(temp);

IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE,leddata);
//printf("temep=%x",temp);
}
//串口中断初始化
void Uart_init()
{
IOWR_ALTERA_AVALON_UART_CONTROL(UART_BASE, 0x80);//接收中断使能
IOWR_ALTERA_AVALON_UART_STATUS(UART_BASE, 0x0);//清状态标志
IOWR_ALTERA_AVALON_UART_RXDATA(UART_BASE, 0x0);//清接收寄存器
alt_irq_register(UART_IRQ,0,Uart_ISR);//中断注册,此处编译总出现警告,
//还请高手能指点。warning: implicit declaration of function
//`alt_irq_register' test3 uart_zx.h,加上中断的头文件#include
}


接着写h文件,注意文件名要与c文件名相同。

UART的h文件

#ifndef UART_H_
#define UART_H_
void Uart_send(unsigned char data);
void Uart_send_n(unsigned char *ptr,unsigned char n);
int Uart_receive(void);
void Uart_ISR(void * context,alt_u32 id);
void Uart_init();
char leddata;

#endif /*UART_H_*/

红色部分是定义头文件的格式

黑色部分是在UART同名的c文件中的函数和变量的声明。

///////////////////////////////////////////////////////////////////////

总结:我们写好一个系统模块的应用程序,想将它分为c文件和h文件,c文件可以按正常的步骤写,写完之后按照h文件的写作格式,在h文件中对c文件的变量和函数进行声明就行。这样我们在含有main函数的c文件中通过#include该头文件,就可以调用该模块的函数了。我们设备的驱动程序也可以这样添加。




工作几年竟让把大学时学的C忘了个七七八八,现用现查。。。。。

相关主题