愛伊米

C語言陷阱與技巧31節,都說void*指標是“萬能指標”,它萬能在哪

在C語言程式開發中,一些比較成熟的庫函式常常會被使用。畢竟,如果手邊就有不錯的“輪子”可以用,沒有程式設計師願意再花費精力憑空造一個輪子出來。

C語言陷阱與技巧31節,都說void*指標是“萬能指標”,它萬能在哪

沒有程式設計師願意憑空造輪子

奇怪的 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語言陷阱與技巧31節,都說void*指標是“萬能指標”,它萬能在哪

編譯器都不知道接下來多少記憶體屬於它

正因為如此,在C語言程式開發中,遇到 void * 指標時,如果需要訪問它指向的記憶體,需要重新指定型別,否則就會報錯。例如下面這段C語言程式碼:

#include

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 指標呢?

C語言陷阱與技巧31節,都說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語言陷阱與技巧31節,都說void*指標是“萬能指標”,它萬能在哪

點個贊再走吧

歡迎在評論區一起討論,質疑。文章都是手打原創,每天最淺顯的介紹C語言、linux等嵌入式開發,喜歡我的文章就關注一波吧,可以看到最新更新和之前的文章哦