在C語言程序開發中,一些比較成熟的庫函數常常會被使用。畢竟,如果手邊就有不錯的「輪子」可以用,沒有程式設計師願意再花費精力憑空造一個輪子出來。
奇怪的 void* 指針
事實上,C語言標準庫提供了非常豐富的成熟函數供程式設計師使用,不過不知道讀者注意到沒,有些庫函數的參數是 void * 類型的,例如:
void *memcpy(void *dest, constvoid *src, size_t n);void *memmove(void *dest, constvoid *src, size_t n);前面的章節在討論C語言指針時,提到指針從某種程度上來說,無非就是一個地址,它的類型只是用於說明數據結構的。例如 int 型指針告訴編譯器該地址處緊接著的 4 字節按照 int 型數據解釋,double 型指針高速編譯器接下來的 8 字節按照 double 型數據解釋。
這裡假定int型變量佔用4位元組內存空間,double 型變量佔用 8 字節內存空間。
對於 void 型指針,之前的分析似乎就不再適用了。因為 void 類型是一個特殊的類型,常被稱作「空類型」,C語言中沒有 void 類型的變量,所以在遇到 void 指針時,編譯器根本不知道如何解釋接下來的內存,甚至編譯器都不知道接下來多少內存屬於它。
正因為如此,在C語言程序開發中,遇到 void * 指針時,如果需要訪問它指向的內存,需要重新指定類型,否則就會報錯。例如下面這段C語言代碼:
#include <stdio.h>void myprint(void *p){char c = p[0];printf("c=%c\n", c);}int main(){char buf[] = "hello world"; myprint(buf);return0;}void myprint(void *p){char c = ((char*)p)[0];printf("c=%c\n", c);}有時候為了簡便,常常使用中間變量:
void myprint(void *p){char *cp = (char*)p;char c = cp[0];printf("c=%c\n", c);}這時再編譯執行就一切正常了。
為什麼使用 void * 指針
僅從上面的實例來看,使用 void 指針似乎比較麻煩:至少強制類型轉換操作是少不了的。那為什麼還要使用 void 指針呢?
仔細分析上面的實例,讀者應注意「在使用 void 型指針時,要將其先轉換為 char 型」,這其實要求程式設計師事先了解 void 指針指向的數據結構(本例是 char 型),否則就沒法使用 void * 指針。
利用這一點,程式設計師可以使用 void * 將不公開的數據隱藏起來,避免外界調用者訪問和修改它。例如,在實際的C語言項目開發中,操作某個對象時,常常先構建結構體 struct S 描述該對象,然後使用 init() 函數獲取相應信息,因為接下來的操作函數 handle() 需知道要操作哪個對象,所以要使用 init() 函數返回的信息,C語言代碼似乎可以這麼寫:
struct S *p = init();handle(p);從上面兩行C語言代碼可以看出,其實外界調用可以不用關心 p 的具體數據結構。不過,如果這麼寫了,外界又的確可以隨意訪問 p 指向的數據結構,這樣就很危險了。事實上,我們完全可以將 p 轉換為 void * 指針:
void *p = init();handle(p);這樣一來,只有 init() 庫的開發者才能訪問 p 指向的內容,避免外界調用者「意外」或者「惡意」的修改,整個C語言程序就會穩定和安全很多了。
另外,void 指針在一些教科書裡被稱作「萬能指針」,這主要是因為任意指針都可以使用 void 指針傳遞,並且編譯器不會報出「類型不匹配」相關的警告。例如,要是將 myprint() 函數的參數類型修改為 int * 型,相關C語言代碼如下:
void myprint(int *p){char *cp = (char*)p;char c = cp[0];printf("c=%c\n", c);}int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);小結
本節拋磚引玉,主要討論了 void 指針在實際C語言項目開發中的特性和用途。void 指針可以將不希望被公開的數據結構隱藏,避免外界調用「意外」或者「惡意」的修改。另外,void * 指針作為「萬能指針」,可以傳遞任意類型的指針,而不引起「類型不匹配」相關的編譯警告。
歡迎在評論區一起討論,質疑。文章都是手打原創,每天最淺顯的介紹C語言、linux等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦