之前對(duì)C語言的頭文件和宏定義抱著一知半解的態(tài)度,現(xiàn)理清思路并以文字的形式整理出來,以供不時(shí)之需
頭文件
頭文件,顧名思義就是定義在C語言文件頭部的那一坨東西
#include <stdio.h>
這就是一個(gè)標(biāo)準(zhǔn)輸入輸出的頭文件聲明,頭文件聲明寫在定義文件(后綴名.c的文件)的頭部,并在定義文件中具體實(shí)現(xiàn)
#include <stdlib.h>#include "mylib.h"
這是兩種聲明頭文件的方法,其中尖括號(hào)表示“到環(huán)境指定的目錄去引用”,而雙引號(hào)表示“首先在當(dāng)前目錄查找,然后在到環(huán)境指定的目錄去引用”
在C表準(zhǔn)庫中每個(gè)庫函數(shù)都在一個(gè)頭文件中聲明,可以通過第一種方式引用
頭文件的格式
#ifndef _MYLIB_H_#define _MYLIB_H_...#endif
第一句“ifndef”意思是“如果在導(dǎo)入頭文件的文件中之前沒有導(dǎo)入該頭文件就編譯下面的代碼”,該句的作用是防止重復(fù)導(dǎo)入
第二句“define”是“宏定義”的意思,表示以下代碼是的頭文件主體部分
最后來一句“endif”和“ifdef”首尾呼應(yīng)
其中“ifndef”和“define”后面跟的是相同的“標(biāo)識(shí)”,通常和頭文件名相同,所有字母均大寫并把點(diǎn)號(hào)改為下劃線即可
#include "mylib.h"
看到這句話后編譯器會(huì)把該頭文件“mylib.h”復(fù)制粘貼到導(dǎo)入的文件中,之后你就可以使用頭文件中定義的常量和結(jié)構(gòu)定義了
顯然恰當(dāng)?shù)厥褂妙^文件有利于更好的組織文件和項(xiàng)目
提請(qǐng)注意
1. 頭文件只是聲明,不占內(nèi)存;在編譯時(shí)會(huì)被合并到源文件
2. 頭文件和其它C語言文件一樣可以引用其它文件,可以寫預(yù)處理塊但是不要出現(xiàn)具體語句
3. 可以在頭文件中定義宏函數(shù),其本質(zhì)上還是一個(gè)聲明
4. 各個(gè)頭文件相互獨(dú)立,標(biāo)準(zhǔn)頭文件之間不存在相互包含關(guān)系
5. 頭文件可以重復(fù)引用,但是相當(dāng)于只導(dǎo)入一次
6. 從C語法角度講完全可以在頭文件中寫任何東西,因?yàn)?code>#include在作用上和Ctrl-C + Ctrl-V
等效——但是這樣時(shí)不推薦的;頭文件的作用就是更好的組織代碼
何時(shí)使用
1. 結(jié)構(gòu)體的定義
2. 函數(shù)聲明,變量聲明,宏定義,常數(shù)定義等
3. 當(dāng)源代碼不便公布時(shí)使用頭文件提供接口
4. 在大項(xiàng)目中需要多文件聯(lián)合編譯
小栗子
#ifndef _NODE_H_#define _NODE_H_typedef struct _node{ int value; struct _node *next;}Node;#endif
#include "node.h"int main(int argc, char const argv[]){ Node *p=(Node*)malloc(sizeof(Node)); ... return 0;}
常用頭文件
stdio.h 標(biāo)準(zhǔn)輸入輸出
stdlib.h 標(biāo)準(zhǔn)常用庫
string.h 字符串函數(shù)庫
math.h 數(shù)學(xué)庫
ctype.h 字符函數(shù)庫
time.h 時(shí)間庫
windows.h 微軟視窗庫
宏定義
宏定義是C語言提供的三種預(yù)處理功能的其中一種,這三種預(yù)處理包括:宏定義、文件包含、條件編譯。宏定義和操作符的區(qū)別是:宏定義是替換,不做計(jì)算,也不做表達(dá)式求解。
“宏定義”也稱“宏替換”,“宏”
define PI 3.1415926
這就是一個(gè)簡(jiǎn)單的宏,在程序的預(yù)處理階段宏名會(huì)被替換為后面的字符串
傳入?yún)?shù)的宏
1. #:字符串化操作,即將宏定義中的傳入?yún)?shù)名轉(zhuǎn)換成用一對(duì)雙引號(hào)括起來參數(shù)名字符串,使用時(shí)置于宏定義體中的參數(shù)名前,如:
#define func(para) #para...char str[]=func(hello); //被展開為:char str[]="hello"
說明:如果傳入的參數(shù)之前有空格則忽略之,如果參數(shù)之間有多個(gè)空格則在連成字符串時(shí)只算一個(gè)
2. #@:字符化操作,即將宏定義傳入的參數(shù)名轉(zhuǎn)換為用一對(duì)單引號(hào)擴(kuò)起來的參數(shù)名字符串,使用時(shí)置于參數(shù)名前,如:
#define fun(pa) #@pachar a=fun(a); //被展開為char a='a';
3. ##:參數(shù)連接操作,即將宏定義的多個(gè)形參連接成一個(gè)實(shí)際參數(shù),如:
#define COMMAND(a,b) a##b...COMMAND(1,2); //相當(dāng)于12CMOOAND(ac,b); //相當(dāng)于acb
4. \:當(dāng)前行繼續(xù)操作,實(shí)際上是對(duì)換行符轉(zhuǎn)義,如:
#define LOOP(FROM, TO, CONTENT)for(int i=FROM;i<TO;i++){CONTENT}
5. _VA_ARGS_:變長(zhǎng)參數(shù)操作,即可以接受不定個(gè)數(shù)的參數(shù),如:
#define eprintf(...) fprintf (stderr, __VA_ARGS__)eprintf ("%s:%d: ", input_file, lineno)//==> fprintf (stderr, "%s:%d: ", input_file, lineno)
為什么要使用宏定義
簡(jiǎn)而言之,使用宏定義可以提高代碼的可讀性
具體的說,可以減少magic number的使用,并且可以做一些方便的替換,如下面的代碼:
#define MALLOC(n, type) (type*)malloc((n)*sizeof(type))
使用時(shí),int *p=MALLOC(10, int);
即可
宏的規(guī)范寫法
1. 宏名大寫
2. 宏定義語句末尾不加分號(hào)
3. 宏函數(shù)的形式參數(shù)不加類型
另外宏定義需要注意的
1. 宏定義可以嵌套
2. 宏定義不能出現(xiàn)在字符串的“”中
3. 宏定義不分配內(nèi)存,變量定義才分配內(nèi)存
4. 宏定義只是簡(jiǎn)單的替換,而且是直接對(duì)源碼的字符串替換,如下面的宏定義就不能很好的表達(dá)求平方函數(shù):
#define sqrt(x) x*x...int y=sqrt(1+2); //y = 1+2*1+2 = 5 ≠9
這時(shí)候加上括號(hào)就好了:
#define sqrt(x) (x)*(x)...int y=sqrt(1+2); //y = (1+2)*(1+2) = 9
5. 宏函數(shù)和自定義函數(shù)相比,效率更高但是安全性低且會(huì)使編譯生成的目標(biāo)文件變大;宏函數(shù)沒有分配和釋放棧幀、傳參、傳返回值等一系列工作,適合那些簡(jiǎn)短并且頻繁調(diào)用的函數(shù),但是對(duì)于遞歸則不推薦使用宏