標(biāo)題:
關(guān)于C語言中.h文件怎么書寫?
[打印本頁]
作者:
浩少成
時間:
2018-12-13 04:34
標(biāo)題:
關(guān)于C語言中.h文件怎么書寫?
寫復(fù)雜程序要不同.h文件。.c.h是對應(yīng)的。
.h文件應(yīng)該怎么寫?應(yīng)該有類似模板一樣的書寫要求吧。
求教
作者:
xuyaqi
時間:
2018-12-13 06:39
可以參看例程,仿照別人的來寫。
作者:
HC6800-ES-V2.0
時間:
2018-12-13 07:58
其實,頭文件的功能就是申明資源,特別是多個C文件的時候,簡單地說,就像是庫文件。
例如:reg51.h頭文件,就是單純的申明資源,系統(tǒng)的其它頭文件,還有申明函數(shù)的。
如果自己編寫頭文件,就可以包含這兩個方面的內(nèi)容了,例如:
發(fā)下是1602器件的頭文件
#ifndef __LCD_H_
#define __LCD_H_
/**********************************
當(dāng)使用的是4位數(shù)據(jù)傳輸?shù)臅r候定義,
使用8位取消這個定義
**********************************/
#define LCD1602_4PINS
/**********************************
包含頭文件
**********************************/
#include<reg51.h>
//---重定義關(guān)鍵詞---//
#ifndef uchar
#define uchar unsigned char
#endif
#ifndef uint
#define uint unsigned int
#endif
/**********************************
PIN口定義
**********************************/
#define LCD1602_DATAPINS P0
sbit LCD1602_E=P2^7;
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;
/**********************************
函數(shù)聲明
**********************************/
/*在51單片機(jī)12MHZ時鐘下的延時函數(shù)*/
void Lcd1602_Delay1ms(uint c); //誤差 0us
/*LCD1602寫入8位命令子函數(shù)*/
void LcdWriteCom(uchar com);
/*LCD1602寫入8位數(shù)據(jù)子函數(shù)*/
void LcdWriteData(uchar dat) ;
/*LCD1602初始化子程序*/
void LcdInit();
#endif
它必須配合1602的C文件同時使用
#include"lcd.h"
/*******************************************************************************
* 函 數(shù) 名 : Lcd1602_Delay1ms
* 函數(shù)功能 : 延時函數(shù),延時1ms
* 輸 入 : c
* 輸 出 : 無
* 說 名 : 該函數(shù)是在12MHZ晶振下,12分頻單片機(jī)的延時。
*******************************************************************************/
void Lcd1602_Delay1ms(uint c) //誤差 0us
{
uchar a,b;
for (; c>0; c--)
{
for (b=199;b>0;b--)
{
for(a=1;a>0;a--);
}
}
}
/*******************************************************************************
* 函 數(shù) 名 : LcdWriteCom
* 函數(shù)功能 : 向LCD寫入一個字節(jié)的命令
* 輸 入 : com
* 輸 出 : 無
*******************************************************************************/
#ifndef LCD1602_4PINS //當(dāng)沒有定義這個LCD1602_4PINS時
void LcdWriteCom(uchar com) //寫入命令
{
LCD1602_E = 0; //使能
LCD1602_RS = 0; //選擇發(fā)送命令
LCD1602_RW = 0; //選擇寫入
LCD1602_DATAPINS = com; //放入命令
Lcd1602_Delay1ms(1); //等待數(shù)據(jù)穩(wěn)定
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5); //保持時間
LCD1602_E = 0;
}
#else
void LcdWriteCom(uchar com) //寫入命令
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 0; //選擇寫入命令
LCD1602_RW = 0; //選擇寫入
LCD1602_DATAPINS = com; //由于4位的接線是接到P0口的高四位,所以傳送高四位不用改
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
// Lcd1602_Delay1ms(1);
LCD1602_DATAPINS = com << 4; //發(fā)送低四位
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 數(shù) 名 : LcdWriteData
* 函數(shù)功能 : 向LCD寫入一個字節(jié)的數(shù)據(jù)
* 輸 入 : dat
* 輸 出 : 無
*******************************************************************************/
#ifndef LCD1602_4PINS
void LcdWriteData(uchar dat) //寫入數(shù)據(jù)
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 1; //選擇輸入數(shù)據(jù)
LCD1602_RW = 0; //選擇寫入
LCD1602_DATAPINS = dat; //寫入數(shù)據(jù)
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5); //保持時間
LCD1602_E = 0;
}
#else
void LcdWriteData(uchar dat) //寫入數(shù)據(jù)
{
LCD1602_E = 0; //使能清零
LCD1602_RS = 1; //選擇寫入數(shù)據(jù)
LCD1602_RW = 0; //選擇寫入
LCD1602_DATAPINS = dat; //由于4位的接線是接到P0口的高四位,所以傳送高四位不用改
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
LCD1602_DATAPINS = dat << 4; //寫入低四位
Lcd1602_Delay1ms(1);
LCD1602_E = 1; //寫入時序
Lcd1602_Delay1ms(5);
LCD1602_E = 0;
}
#endif
/*******************************************************************************
* 函 數(shù) 名 : LcdInit()
* 函數(shù)功能 : 初始化LCD屏
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
#ifndef LCD1602_4PINS
void LcdInit() //LCD初始化子程序
{
LcdWriteCom(0x38); //開顯示
LcdWriteCom(0x0c); //開顯示不顯示光標(biāo)
LcdWriteCom(0x06); //寫一個指針加1
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //設(shè)置數(shù)據(jù)指針起點
}
#else
void LcdInit() //LCD初始化子程序
{
LcdWriteCom(0x32); //將8位總線轉(zhuǎn)為4位總線
LcdWriteCom(0x28); //在四位線下的初始化
LcdWriteCom(0x0c); //開顯示不顯示光標(biāo)
LcdWriteCom(0x06); //寫一個指針加1
LcdWriteCom(0x01); //清屏
LcdWriteCom(0x80); //設(shè)置數(shù)據(jù)指針起點
}
#endif
觀察注意到:C文件中的資源、函數(shù),都是通過頭文件申明的,這樣才能在主C文件中,調(diào)用到1602的C文件中的函數(shù)
你看主程序是這樣的:
#include<reg51.h>
#include"lcd.h"
unsigned char PuZh[]=" Pechin Science ";
/*******************************************************************************
* 函 數(shù) 名 : main
* 函數(shù)功能 : 主函數(shù)
* 輸 入 : 無
* 輸 出 : 無
*******************************************************************************/
void main(void)
{
unsigned char i;
LcdInit();
for(i=0;i<16;i++)
{
LcdWriteData(PuZh[i]);
}
while(1)
{
}
}
知道了自編頭文件的作用,就可以按照上述格式,編寫自己的其它器件的C文件和頭文件了。
作者:
angmall
時間:
2018-12-13 08:41
C語言中.h和.c文件解析
簡單的說其實要理解C文件與頭文件(即.h)有什么不同之處,首先需要弄明白編譯器的工作過程,一般說來編譯器會做以下幾個過程:
1.預(yù)處理階段
2.詞法與語法分析階段
3.編譯階段,首先編譯成純匯編語句,再將之匯編成跟CPU相關(guān)的二進(jìn)制碼,生成各個目標(biāo)文件 (.obj文件)
4.連接階段,將各個目標(biāo)文件中的各段代碼進(jìn)行絕對地址定位,生成跟特定平臺相關(guān)的可執(zhí)行文件,當(dāng)然,最后還可以用objcopy生成純二進(jìn)制碼,也就是去掉了文件格式信息。(生成.exe文件)
編譯器在編譯時是以C文件為單位進(jìn)行的,也就是說如果你的項目中一個C文件都沒有,那么你的項目將無法編譯,連接器是以目標(biāo)文件為單位,它將一個或多個目標(biāo)文件進(jìn)行函數(shù)與變量的重定位,生成最終的可執(zhí)行文件,在PC上的程序開發(fā),一般都有一個main函數(shù),這是各個編譯器的約定,當(dāng)然,你如果自己寫連接器腳本的話,可以不用main函數(shù)作為程序入口!。!
。╩ain .c文件 目標(biāo)文件 可執(zhí)行文件)
有了這些基礎(chǔ)知識,再言歸正傳,為了生成一個最終的可執(zhí)行文件,就需要一些目標(biāo)文件,也就是需要C文件,而這些C文件中又需要一個main函數(shù)作為可執(zhí)行程序的入口,那么我們就從一個C文件入手,假定這個C文件內(nèi)容如下:
#include <stdio.h>
#include "mytest.h"
int main(int argc, char **argv)
{
test = 25;
printf("test.................%d\n",test);
}
復(fù)制代碼
mytest.h頭文件內(nèi)容如下:
int test;
復(fù)制代碼
現(xiàn)在以這個例子來講解編譯器的工作:
1.預(yù)處理階段:編譯器以C文件作為一個單元,首先讀這個C文件,發(fā)現(xiàn)第一句與第二句是包含一個頭文件,就會在所有搜索路徑中尋找這兩個文件,找到之后,就會將相應(yīng)頭文件中再去處理宏,變量,函數(shù)聲明,嵌套的頭文件包含等,檢測依賴關(guān)系,進(jìn)行宏替換,看是否有重復(fù)定義與聲明的情況發(fā)生,最后將那些文件中所有的東東全部掃描進(jìn)這個當(dāng)前的C文件中,形成一個中間"C文件"
2.編譯階段,在上一步中相當(dāng)于將那個頭文件中的test變量掃描進(jìn)了一個中間C文件,那么test變量就變成了這個文件中的一個全局變量,此時就將所有這個中間C文件的所有變量,函數(shù)分配空間,將各個函數(shù)編譯成二進(jìn)制碼,按照特定目標(biāo)文件格式生成目標(biāo)文件,在這種格式的目標(biāo)文件中進(jìn)行各個全局變量,函數(shù)的符號描述,將這些二進(jìn)制碼按照一定的標(biāo)準(zhǔn)組織成一個目標(biāo)文件
3.連接階段,將上一步成生的各個目標(biāo)文件,根據(jù)一些參數(shù),連接生成最終的可執(zhí)行文件,主要的工作就是重定位各個目標(biāo)文件的函數(shù),變量等,相當(dāng)于將個目標(biāo)文件中的二進(jìn)制碼按一定的規(guī)范合到一個文件中再回到C文件與頭文件各寫什么內(nèi)容的話題上:理論上來說C文件與頭文件里的內(nèi)容,只要是C語言所支持的,無論寫什么都可以的,比如你在頭文件中寫函數(shù)體,只要在任何一個C文件包含此頭文件就可以將這個函數(shù)編譯成目標(biāo)文件的一部分(編譯是以C文件為單位的,如果不在任何C文件中包含此頭文件的話,這段代碼就形同虛設(shè)),你可以在C文件中進(jìn)行函數(shù)聲明,變量聲明,結(jié)構(gòu)體聲明,這也不成問題。。∧菫楹我欢ㄒ殖深^文件與C文件呢?又為何一般都在頭件中進(jìn)行函數(shù),變量聲明,宏聲明,結(jié)構(gòu)體聲明呢?而在C文件中去進(jìn)行變量定義,函數(shù)實現(xiàn)呢??原因如下:
1.如果在頭文件中實現(xiàn)一個函數(shù)體,那么如果在多個C文件中引用它,而且又同時編譯多個C文件,將其生成的目標(biāo)文件連接成一個可執(zhí)行文件,在每個引用此頭文件的C文件所生成的目標(biāo)文件中,都有一份這個函數(shù)的代碼,如果這段函數(shù)又沒有定義成局部函數(shù),那么在連接時,就會發(fā)現(xiàn)多個相同的函數(shù),就會報錯
2.如果在頭文件中定義全局變量,并且將此全局變量賦初值,那么在多個引用此頭文件的C文件中同樣存在相同變量名的拷貝,關(guān)鍵是此變量被賦了初值,所以編譯器就會將此變量放入DATA段,最終在連接階段,會在DATA段中存在多個相同的變量,它無法將這些變量統(tǒng)一成一個變量,也就是僅為此變量分配一個空間,而不是多份空間,假定這個變量在頭文件沒有賦初值,編譯器就會將之放入 BSS段,連接器會對BSS段的多個同名變量僅分配一個存儲空間
3.如果在C文件中聲明宏,結(jié)構(gòu)體,函數(shù)等,那么我要在另一個C文件中引用相應(yīng)的宏,結(jié)構(gòu)體,就必須再做一次重復(fù)的工作,如果我改了一個C文件中的一個聲明,那么又忘了改其它C文件中的聲明,這不就出了大問題了,程序的邏輯就變成了你不可想象的了,如果把這些公共的東東放在一個頭文件中,想用它的C文件就只需要引用一個就OK了。!這樣豈不方便,要改某個聲明的時候,只需要動一下頭文件就行了
4.在頭文件中聲明結(jié)構(gòu)體,函數(shù)等,當(dāng)你需要將你的代碼封裝成一個庫,讓別人來用你的代碼,你又不想公布源碼,那么人家如何利用你的庫呢?也就是如何利用你的庫中的各個函數(shù)呢??一種方法是公布源碼,別人想怎么用就怎么用,另一種是提供頭文件,別人從頭文件中看你的函數(shù)原型,這樣人家才知道如何調(diào)用你寫的函數(shù),就如同你調(diào)用printf函數(shù)一樣,里面的參數(shù)是怎樣的??你是怎么知道的??還不是看人家的頭文件中的相關(guān)聲明啊。!當(dāng)然這些東東都成了C標(biāo)準(zhǔn),就算不看人家的頭文件,你一樣可以知道怎么使用
作者:
angmall
時間:
2018-12-13 08:45
c語言中.c和.h文件的困惑
本質(zhì)上沒有任何區(qū)別。 只不過一般:.h文件是頭文件,內(nèi)含函數(shù)聲明、宏定義、結(jié)構(gòu)體定義等內(nèi)容
.c文件是程序文件,內(nèi)含函數(shù)實現(xiàn),變量定義等內(nèi)容。而且是什么后綴也沒有關(guān)系,只不過編譯器會默認(rèn)對某些后綴的文件采取某些動作。你可以強(qiáng)制編譯器把任何后綴的文件都當(dāng)作c文件來編。
這樣分開寫成兩個文件是一個良好的編程風(fēng)格。
作者:
angmall
時間:
2018-12-13 09:02
合理地使用.h文件能夠很好地理清項目工程的結(jié)構(gòu)和提高編譯的效率。頭文件主要是對函數(shù)、全局變量的聲明和一些宏的定義,.h文件是不參與編譯的,#include宏的作用就是預(yù)處理的時候在使用這句話的地方用.h文件的內(nèi)容替換掉這句話。
聲明的作用也只是告訴編譯器,某個函數(shù)或者變量符合在調(diào)用之前在程序的某處已經(jīng)定義過,編譯的時候不報錯,
#if !defined
和
#endif
兩個指令主要哦是為了避免在同一個編譯模塊(一個.c文件)中避免重復(fù)包含同一個.h文件。
歡迎光臨 (http://www.torrancerestoration.com/bbs/)
Powered by Discuz! X3.1