頭文件是c語言中重要的文件類型,用於保存程序的聲明。函數原型一般都放在頭文件中,函數定義則放在源文件中,當源文件或頭文件通過#include指令包含另一個頭文件的時候,編譯預處理器用頭文件的內容取代#include僞指令。這就是說,頭文件的所有內容最終都會被合併到某一個或某幾個源文件中。
頭文件的作用:
(1)通過頭文件來調用庫功能。在很多場合,源代碼不便(或不準)向用戶公佈,只要向用戶提供頭文件和二進制的庫即可。用戶只需按照頭文件中的接口聲明來調用庫函數,而不必關心接口是怎麼實現的。連接器會從庫中提取相應的代碼,並和用戶的程序連接生成可執行文件或者動態連接庫文件;
(2)頭文件能加強類型安全檢查。如果某個接口被實現或被使用時的方式與頭文件中的聲明不一致,編譯器就會指出錯誤,這一簡單的規則能大大減輕程序員調試、改錯的負擔;
(3)頭文件可以提高程序的可讀性(清晰性)。
那麼怎麼寫好頭文件呢,哪些東西應該放在頭文件中呢,讓我們依次來看看。
頭文件中的元素比較多,其順序(結構)一般應安排如下:
(1)頭文件註釋(包括文件說明、功能描述、版權聲明等)(必須有);
/*********************************************************************
; Project : xxx
; File Name : xxx
; Vesion number : 1.00
; File History : Revision 1.00 2010/07/30 frist creation
; Auther : xxx
; Brief : xxx
**********************************************************************/
(2)內部包含衛哨開始(#ifndef XXX/#define XXX)(必須有);
#ifndef _COMMON_H_
#define _COMMON_H_
... ...
(3)#include其他頭文件(如果需要);
#include "..//xxx//xxx.h"
(4)外部變量和全局函數聲明(如果需要);
extern ... ...
Note:在.h文件中聲明的函數,如果在其對應的.c文件中有定義,那麼我們在聲明這個函數時,不使用extern修飾符, 如果反之,則必須顯示使用extern修飾符.
(5)常量和宏定義(如果需要);
(6)類型前置聲明和定義(如果需要);
(7)全局函數原型和內聯函數的定義(如果需要);
Note:如果程序中需要內聯函數,那麼內聯函數的定義應當放在頭文件中,因爲內聯函數調用語句最終被擴展開來而不是採用真正的函數調用機制。
(8)內部包含衛哨結束:#endif // XXX(必須有);
... ...
#endif
(9)文件版本及修訂說明。
上述排列順序並非絕對,也不存在對錯之分,可根據具體情況靈活安排。
基於頭文件的作用,需要先來談談C語言的存儲類說明符: auto, extern, static.
單一源文件中的 auto,extern,static
auto: 變量的默認存儲類別就是auto,例如 int num; 等同於 auto int num; 不用顯示指定。
我們知道,在一對{ }中的代碼段叫做一個塊(block), 程序運行到一個auto包含變量的時候,
會自動給變量分配存儲空間,在離開塊的時候會自動釋放空間, 例如調用一個函數之後,函數塊
內的局部變量空間將會釋放。 auto 強調存儲空間的自動分配與釋放。
extern: 在函數體外定義的變量默認爲extern類型。extern變量必須定義在所有函數外面。在程序開始之前被分配空間,在執行時不被釋放。可以在定義時初始化,也可以由系統在分配存儲空間時初始化爲0. 其只被初始化一次。在其後面的所有函數,都可以訪問該變量。
static: 靜態變量. 必須顯示指定其類行爲static.程序執行前分配空間,執行時不會釋放,如果不初始化,則系統在分配空間時初識化爲0,同extern類似,只被初始化一次,這意味着,如下函數的count變量在多次調用函數時會遞增。
void cnt()
{
static int count=0;// 無論調用cnt()多少次,count只被初始化一次。
count++;
}
靜態變量只在包含它的程序塊中可見。
如果在某個塊內定義的auto變量或者static變量與一個extern變量同名,則在塊內只有auto或者static可見,同名extern被屏蔽。
多文件中的存儲類別
主要是針對extern關鍵字.
關於定義和聲明的區別.對於auto,static和register變量來說,定義和聲明相一致,所以統稱定義/聲明.
使用extern 關鍵字可以使變量被其他文件訪問。
在文件A中定義:
int a=10; 或者 extern int a=10;
注意:如果不使用extern 關鍵字定義,則是否初始化都可以,如果使用了extern,則必須初始化,
extern int a; 這樣的定義是錯誤的.
如何引用? 在文件B中,或者在定義前面的函數中, 變量a是不可見的,如果在要使用的塊內使用extern聲明
extern int a; 之後就可以使用了。
比如
文件A: int a=10; //定義
文件B: extern int a; //聲明
printf("%d",a);//使用
STATIC關鍵字: 它即可以在函數內定義,也可以在函數外定義. 但無論如何,都不會被其他文件訪問,
所以static 可以用來保護數據不被外部文件訪問。
頭文件中重要的東西舉例:
1。 struct 數據結構定義
struct swp_ctrl_err{
U8 atout;
U8 gdtout;
U8 rsttout;
};
struct swp_stru{
U8 swp_state;
U8 pwr_mode;
U8 ep_cap;
U8 ACT_last_frame;
U8 SHDLC_linked;
U8 rxrdy;
U8 activated_ever;
U8 exp_clf_ns;
struct swp_ctrl_err ctrl_err;
};
extern volatile struct swp_stru swp_info;
數據結構的定義必須放在.h的頭文件中,變量聲明則在c文件中:volatile struct swp_stru swp_info;
2。內聯函數亦應該放在.h文件中,這取決於內聯函數的機制:內聯函數調用語句最終被擴展開來而不是採用真正的函數調用機制
__inline void enter_idle(void)
{
int tmp;
tmp=0; //Enter the idle mode, CPU clock stop
__asm
{
MCR p15, 0, tmp, c7, c0, 4
NOP
NOP
NOP
NOP
NOP
NOP
}
}
最後介紹幾個常用且很有用的宏定義。
//類型相關
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned char U8;
typedef unsigned short U16;
typedef unsigned long U32;
typedef signed char S8;
typedef signed short S16;
typedef signed long S32;
//字節操作相關
#define HiByte(x) *((uchar *)&x)
#define LoByte(x) *(((uchar *)&x)+1)
#define BYTE(x) *((uchar *)&x)
#define WORD(x) *((ushort *)&x)
#define LO_NIBBLE 0x0F
#define HI_NIBBLE 0xF0
//位操作相關
#define BIT_SET(x,n) (x=x | (0x01<<n))
#define BIT_TEST(x,n) ((x & (0x01<<n))!=0)
#define BIT_CLEAR(x,n) (x=x & ~(0x01<<n))