深刻理解void類型

 1.空指針 程序員

    通常來講,程序的起始地址是從「代碼區」的0地址開始存放的(注:若是插入一個內存分佈圖,則更能說明問題,此處省略),但實際上現代操做系統並不是如此,卻保留了從0開始的一塊內存。至於這塊內存到底有有多大,與具體的操做系統有關。若是程序試圖訪問這塊內存,則系統提示異常。 函數

    爲何操做系統不是保留一個字節呢?因爲內存管理是按頁來進行的,所以沒法作到單獨保留一個字節。儘管如此,但仍是有極少數系統設定RAM區從0地址開始,但指向有效變量的指針不會指向0地址。即便「代碼區」從0地址開始,但在任何狀況下,0地址都不是C語言中任何函數的起始地址,所以指向有效函數地址的指針也不會指向0地址。 spa

       ☛ 課外知識延伸 操作系統

    雖然 80C51微控制器XDATA區(外部RAM)是從0地址開始的,但只要對保存在0地址中的變量不進行取地址操做(&操做),便可有效地保證指針不會指向0地址。 指針

    與此同時,雖然32ARM7微控制器也是從0地址開始的,但這塊內存僅用於存放中斷向量代碼,而不是程序中的有效變量地址,所以即使用空指針來判斷指針的有效性,其仍然是可行的。 orm

    基於此,因而將空指針定義爲指向0地址的指針。毫無疑問,任何一種指針類型都有一個特殊的指針值,即空指針。它既不會指向任何對象或函數,也不是任何對象或函數的地址。而未初始化的指針,則徹底可能指向任何地方。 對象

    因而可知,空指針與未初始化的指針是徹底不一樣的兩個概念。那麼,將如何在程序中得到一個空指針呢? 內存

 

2.       空指針常量與NULL 原型

    標準C規定,在初始化、賦值或比較時,若是一邊是變量或指針類型的表達式,則編譯器能夠肯定另外一邊的常數0爲空指針,並生成正確的空指針值。即在指針上下文中「值爲0的整型常量表達式」在編譯時轉換爲空指針。 編譯器

    爲了讓程序中的空指針使用更加明確,標準C專門定義了一個標準預處理宏NULL其值爲「空指針常量」,一般爲0(void *)0,即在指針上下文中NULL0是等價的,而未加修飾的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;    // 定義pucBufunsigned char類型指針並初始化爲空指針

    若是後續的代碼忘記初始化指針而直接使用的話,則可能形成程序失敗。雖然空指針也是非法指針,但能夠經過程序判斷並告訴程序員代碼可能有問題。也就是說,若是一開始就將指針初始化爲空指針,則可避免程序異常。好比:

         if(pucBuf==0){

            return error;                     // 若是pucBuf爲空指針,則返回參數錯誤

         }

    因爲void類型指針的不肯定性,所以它能夠指向任意類型的數據,那麼只要在使用時作一個簡單的強制類型轉換就能夠了。好比:

         unsignned char *pcData = NULL;      // 定義pcDataunsigned char類型指針

         void           *pvData;             // 定義pvDatavoid類型指針

        

         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

相關文章
相關標籤/搜索