一些初學(xué)C語言的人,不知道頭文件(*.h文件)原來還可以自己寫的。只知道調(diào)用系統(tǒng)庫函數(shù)時(shí),要使用#include語句將某些頭文件包含進(jìn)去。其實(shí),頭文件跟.C文件一樣,是可以自己寫的。頭文件是一種文本文件,使用文本編輯器將代碼編寫好之后,以擴(kuò)展名.h保存就行了。頭文件中一般放一些重復(fù)使用的代碼,例如函數(shù)聲明,變量聲明,常數(shù)定義,宏的定義等等。當(dāng)使用#include語句將頭文件引用時(shí),相當(dāng)于將頭文件中所有內(nèi)容,復(fù)制到#include處。為了避免因?yàn)橹貜?fù)引用而導(dǎo)致的編譯錯(cuò)誤,頭文件常具有
#ifndef LABEL
#define LABEL
//代碼部分
#endif
的格式。其中,LABEL為一個(gè)唯一的標(biāo)號(hào),命名規(guī)則跟變量的命名規(guī)則一樣。常根據(jù)它所在的頭文件名來命名,例如,如果頭文件的文件名叫做hardware.h,那么可以這樣使用:
#ifndef __HARDWARE_H__
#define __HARDWARE_H__
//代碼部分
#endif
這樣寫的意思就是,如果沒有定義__HARDWARE_H__,則定義__HARDWARE_H__,并編譯下面的代碼部分,直到遇到#endif。這樣,當(dāng)重復(fù)引用時(shí),由于__HARDWARE_H__已經(jīng)被定義,則下面的代碼部分就不會(huì)被編譯了,這樣就避免了重復(fù)定義。
另外一個(gè)地方就是使用include時(shí),使用引號(hào)與尖括號(hào)的意思是不一樣的。使用引號(hào)(“”)時(shí),首先搜索工程文件所在目錄,然后再搜索編譯器頭文件所在目錄。而使用尖括號(hào)(<>)時(shí),剛好是相反的搜索順序。假設(shè)我們有兩個(gè)文件名一樣的頭文件hardware.h,但內(nèi)容卻是不一樣的。一個(gè)保存在編譯器指定的頭文件目錄下,我們把它叫做文件I;另一個(gè)則保存在當(dāng)前工程的目錄下,我們把它叫做文件II。如果我們使用的是#include <hardware.h>,則我們引用到的是文件I。如果我們使用的是#include “hardware.h”,則我們引用的將是文件II。筆者以前就遇到過一個(gè)同事問,為什么他修改了那個(gè)頭文件里面的內(nèi)容,好象跟沒有修改一樣?就是因?yàn)樗袃蓚€(gè)一樣的頭文件(就像我們剛描述的那樣),他是使用#include <hardware.h>引用的,而他修改時(shí),卻是當(dāng)前工程所在的目錄下的那個(gè)文件。
筆者所使用的編寫c程序的IDE是CodeBlokcs, 我的頭文件放置在D:\CodeBlocks\MinGW\include文件夾下。
我們在編寫C語言或C++語言時(shí),預(yù)處理文件都被表示為#include <stdio.h>、#include <iostream.h>或#include <string.h>等等。在這里的“h”表示的是頭文件。那么是什么的頭文件?頭文件又是什么呢?如若不加上“h”的話,我運(yùn)行了程序,似乎還是可以運(yùn)行成功,但加不加“h”到底又有什么關(guān)系? #include 語句起的是文件包含作用。功能是把指定的文件插入該命令行位置取代該命令行,從而把指定的文件和當(dāng)前的源程序文件連成一個(gè)源文件。要求這個(gè)語句必須在源程序的最前面。
所謂“頭文件”,指的是在源文件頭部引入的文件,顧名思義,即是頭文件。至于頭文件本身,則沒有什么固定格式和固定擴(kuò)展名。你可以將自己寫的C文件作為頭文件包含進(jìn)來。至于擴(kuò)展名h,則表示是系統(tǒng)提供的頭文件類型。如果是TC,一般頭文件的路徑是:TC/INCLUDE 。 頭文件都是純文本格式,內(nèi)容多數(shù)是函數(shù)定義或函數(shù)體等。
在調(diào)用系統(tǒng)函數(shù)時(shí),有些是必須要包含進(jìn)來相應(yīng)的頭文件才可以使用的,如isalpha函數(shù),判斷是否為字母,必須要包含ctype.h頭文件。有些則不必,那是因?yàn)橄到y(tǒng)已經(jīng)默認(rèn)包含進(jìn)來了,例如包含Printf函數(shù)的頭文件stdio.h,是系統(tǒng)默認(rèn)包含的,雖然顯示的包含也沒有任何問題。
另外,對(duì)于系統(tǒng)提供的頭文件,如ctype.h 如果不寫擴(kuò)展名的話,系統(tǒng)會(huì)找不到這個(gè)頭文件,而無法實(shí)現(xiàn)包含的功能。所以正確的系統(tǒng).h文件,是一定要寫上擴(kuò)展名的。
頭文件random_number.h(生成隨機(jī)數(shù))
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#ifndef RANDOMNUMBER_H_INCLUDED
#define RANDOMNUMBER_H_INCLUDED
#endif // RANDOMNUMBER_H_INCLUDED
//隨機(jī)數(shù)庫實(shí)現(xiàn)
void Randomize()
{
srand((unsigned)time(NULL)); //使用當(dāng)前時(shí)間作為偽隨機(jī)數(shù)種子
}
int GenerateRandomNumber(int low, int high)
{
double _d;
if(low > high)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED);
// 打印出紅色字體
printf("Make sure input number: Low < High .\n");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
// 打印默認(rèn)字體
exit(0);
}
_d = (double)rand()/((double)RAND_MAX + 1.0);
return low + (int)(_d * (high - low +1));
}
double GenerateRandomReal(double low, double high)
{
double _d;
if(low > high)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED);
printf("Make sure input number: Low < High .\n");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
exit(0);
}
_d = (double)rand()/((double)RAND_MAX + 1.0);
return low + _d * (high - low);
}
現(xiàn)在我們只需要在需要使用到隨機(jī)數(shù)的文件中寫上
#ifndef RANDOMNUMBER_H_INCLUDED
#include "random_number.h"
#endif // RANDOMNUMBER_H_INCLUDED
即可使用上面的函數(shù)調(diào)用了。
頭文件(random_number.h)進(jìn)階
上面的文件是只寫了一個(gè)頭文件并且將定義和實(shí)現(xiàn)都寫在了頭文件中,其實(shí)我們可以將定義和實(shí)現(xiàn)分離(為了更好的封裝性,讓用戶專注于接口的使用,而忽略實(shí)現(xiàn)的細(xì)節(jié))。
頭文件(random_number.h)中代碼如下:
#ifndef RANDOM_H_INCLUDED
#define RANDOM_H_INCLUDED
void Randomize(); //初始化偽隨機(jī)數(shù)發(fā)生器
int GenerateRandomNumber(int low, int high); //生成一個(gè)隨機(jī)整數(shù)介于low 和 high 之間
double GenerateRandomReal(double low, double high); //生成一個(gè)隨機(jī)實(shí)數(shù)介于low 和 high 之間
#endif // RANDOM_H_INCLUDED
同時(shí)需要一個(gè).c文件來實(shí)現(xiàn)在頭文件中定義的函數(shù)。(random_number.c)具體代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#ifndef RANDOM_H_INCLUDED
#include "random.h"
#endif
//隨機(jī)數(shù)庫實(shí)現(xiàn)
void Randomize()
{
srand((unsigned)time(NULL)); //使用當(dāng)前時(shí)間作為偽隨機(jī)數(shù)種子
}
int GenerateRandomNumber(int low, int high)
{
double _d;
if(low > high)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED);
// 打印出紅色字體
printf("Make sure input number: Low < High .\n");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
// 打印默認(rèn)字體
exit(0);
}
_d = (double)rand()/((double)RAND_MAX + 1.0);
return low + (int)(_d * (high - low +1));
}
double GenerateRandomReal(double low, double high)
{
double _d;
if(low > high)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED);
printf("Make sure input number: Low < High .\n");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
exit(0);
}
_d = (double)rand()/((double)RAND_MAX + 1.0);
return low + _d * (high - low);
}
這個(gè)庫與上一種寫法使用方法相同。但我們更推薦這種寫法