1.空指針 程序員
通常來講,程序的起始地址是從「代碼區」的0地址開始存放的(注:若是插入一個內存分佈圖,則更能說明問題,此處省略),但實際上現代操做系統並不是如此,卻保留了從0開始的一塊內存。至於這塊內存到底有有多大,與具體的操做系統有關。若是程序試圖訪問這塊內存,則系統提示異常。 函數
爲何操做系統不是保留一個字節呢?因爲內存管理是按頁來進行的,所以沒法作到單獨保留一個字節。儘管如此,但仍是有極少數系統設定RAM區從0地址開始,但指向有效變量的指針不會指向0地址。即便「代碼區」從0地址開始,但在任何狀況下,0地址都不是C語言中任何函數的起始地址,所以指向有效函數地址的指針也不會指向0地址。 spa
☛ 課外知識延伸 操作系統
雖然 80C51微控制器XDATA區(外部RAM)是從0地址開始的,但只要對保存在0地址中的變量不進行取地址操做(&操做),便可有效地保證指針不會指向0地址。 指針
與此同時,雖然32位ARM7微控制器也是從0地址開始的,但這塊內存僅用於存放中斷向量代碼,而不是程序中的有效變量地址,所以即使用空指針來判斷指針的有效性,其仍然是可行的。 orm
基於此,因而將空指針定義爲指向0地址的指針。毫無疑問,任何一種指針類型都有一個特殊的指針值,即空指針。它既不會指向任何對象或函數,也不是任何對象或函數的地址。而未初始化的指針,則徹底可能指向任何地方。 對象
因而可知,空指針與未初始化的指針是徹底不一樣的兩個概念。那麼,將如何在程序中得到一個空指針呢? 內存
2. 空指針常量與NULL 原型
標準C規定,在初始化、賦值或比較時,若是一邊是變量或指針類型的表達式,則編譯器能夠肯定另外一邊的常數0爲空指針,並生成正確的空指針值。即在指針上下文中「值爲0的整型常量表達式」在編譯時轉換爲空指針。 編譯器
爲了讓程序中的空指針使用更加明確,標準C專門定義了一個標準預處理宏NULL,其值爲「空指針常量」,一般爲0或(void *)0,即在指針上下文中NULL與0是等價的,而未加修飾的0也是徹底能夠接受的。因爲void *指針的特殊賦值屬性,好比:
#define NULL ((void *)0)
當NULL定義爲((void *)0)時,即NULL是能夠賦值給任何類型指針的值,它的類型爲void*,而不是整數0,所以初始化「FILE *fp = NULL;」是徹底合法的。
而爲了區分整數0和空指針0,當須要其它類型的0的時候,即便可能工做,但也不能使用NULL,若是這樣處理其格式是錯誤的,這在非指針上下文中是不能工做的。特別地,不能在須要ASCII空字符(NUL)的地方使用NULL。若是確實須要,則能夠自定義爲:
#define NUL '\0'
因而可知,常數0是一個空指針常量,而NULL僅僅是它的一個別名。
3. 空指針的用途
通常來講,未初始化是不能使用的非法指針,由於它徹底有可能指向任何地方,從而致使程序沒法判斷它爲非法指針。所以,無論指針變量是全局的仍是局部的、靜態的仍是非靜態的,都應該在聲明它的同時進行初始化,要麼賦予一個有效的地址,要麼賦予NULL。
標準C規定,全局指針變量的默認值爲NULL,而對於局部指針變量則必須明確地指定其初值。所以,void一般用於指針變量的初始化,用來判斷一個指針的有效性。好比:
unsigned char *pucBuf=(void *)0; // 定義pucBuf爲unsigned char類型指針並初始化爲空指針
若是後續的代碼忘記初始化指針而直接使用的話,則可能形成程序失敗。雖然空指針也是非法指針,但能夠經過程序判斷並告訴程序員代碼可能有問題。也就是說,若是一開始就將指針初始化爲空指針,則可避免程序異常。好比:
if(pucBuf==0){
return error; // 若是pucBuf爲空指針,則返回參數錯誤
}
因爲void類型指針的不肯定性,所以它能夠指向任意類型的數據,那麼只要在使用時作一個簡單的強制類型轉換就能夠了。好比:
unsignned char *pcData = NULL; // 定義pcData爲unsigned char類型指針
void *pvData; // 定義pvData爲void類型指針
pvData = pcData; // 無需進行強制類型轉換
pcData = (unsigned char*) pvData; // 將pvData強制轉換爲unsigned char類型指針
顯然不存在void類型的對象,也就是說,當對象爲空類型時,其大小爲0字節;當對象未肯定類型時,那麼它的大小也是未肯定的,所以不能聲明void類型變量。好比:
void a; // 非法聲明
既然上述聲明是非法的,那麼,也就不能將sizeof運算符用於void類型。也就意味着,編譯器不知道所指對象的大小,因爲指針的算術運算老是基於所指對象的大小的,所以不容許對void指針進行算術運算。
總之,在指針聲明中,void *表示通用指針的類型。若是void做爲函數的返回類型,則表示不返回任何值。若是void位於參數列表中,則表示沒有參數。
4. 用無類型指針做爲函數參數
因爲C語言中最小長度的變量爲char類型(包括unsigned char、signed char等),其sizeof(char)的結果爲1,而其它任何變量的長度都是它的整數倍。好比,若是使用SDCC51編譯器,其sizeof(int)爲2。由於通用swap函數函數不知道須要交換的變量的類型,因此須要一個參數給出相應的指示。因爲C語言的變量類型多種多樣,所以不可能爲每一種變量類型編號,並且swap並不關心變量的真正類型,因此能夠用變量的長度代替變量類型。通用swap函數的原型爲:
void swap(void *pvData1, void *pvData2, int iDataSize