void在英文中作為名詞的解釋為“空虛;空間;空隙”;而在C語言中,void為“無類型”,相應的void *為“無類型指針”。void似乎只有“注釋”和限制程序的作用,當然,這裡的“注釋”不是為我們人提供注釋,而是為編譯器提供一種所謂的注釋。
本文地址:http://www.cnblogs.com/archimedes/p/c-void-point.html,轉載請注明源地址。
1.對函數返回的限定,這種情況我們比較常見。
2.對函數參數的限定,這種情況也是比較常見的。
一般我們常見的就是這兩種情況:
當函數不需要返回值值時,必須使用void限定,這就是我們所說的第一種情況。例如:void func(int a,char *b)。
當函數不允許接受參數時,必須使用void限定,這就是我們所說的第二種情況。例如:int func(void)。
1.void指針可以指向任意類型的數據,就是說可以用任意類型的指針對void指針對void指針賦值。例如:
int *a;
void *p;
p=a;
如果要將void指針p賦給其他類型的指針,則需要強制類型轉換,就本例而言:a=(int *)p。在內存的分配中我們可以見到void指針使用:內存分配函數malloc函數返回的指針就是void *型,用戶在使用這個指針的時候,要進行強制類型轉換,也就是顯式說明該指針指向的內存中是存放的什麼類型的數據(int *)malloc(1024)表示強制規定malloc返回的void*指針指向的內存中存放的是一個個的int型數據。
2.在ANSI C標准中,不允許對void指針進行一些算術運算如p++或p+=1等,因為既然void是無類型,那麼每次算術運算我們就不知道該操作幾個字節,例如char型操作sizeof(char)字節,而int則要操作sizeof(int)字節。而在GNU中則允許,因為在缺省情況下,GNU 認為void *和char *一樣,既然是確定的,當然可以進行一些算術操作,在這裡sizeof(*p)==sizeof(char)。
void幾乎只有“注釋”和限制程序的作用,因為從來沒有人會定義一個void變量,讓我們試著來定義:
void a;
這行語句編譯時會出錯,提示“illegal use of type 'void'”。即使void a的編譯不會出錯,它也沒有任何實際意義。
眾所周知,如果指針p1和p2的類型相同,那麼我們可以直接在p1和p2間互相賦值;如果p1和p2指向不同的數據類型,則必須使用強制類型轉換運算符把賦值運算符右邊的指針類型轉換為左邊指針的類型。
float *p1; int *p2; p1 = p2; //其中p1 = p2語句會編譯出錯, //提示“'=' : cannot convert from 'int *' to 'float *'”,必須改為: p1 = (float *)p2;
而void *則不同,任何類型的指針都可以直接賦值給它,無需進行強制類型轉換
void *p1; int *p2; p1 = p2;
但這並不意味著,void *也可以無需強制類型轉換地賦給其它類型的指針。因為“無類型”可以包容“有類型”,而“有類型”則不能包容“無類型”。
小心使用void指針類型:
按照ANSI(American National Standards Institute)標准,不能對void指針進行算法操作,即下列操作都是不合法的:
void * pvoid; pvoid++; //ANSI:錯誤 pvoid += 1; //ANSI:錯誤 //ANSI標准之所以這樣認定,是因為它堅持:進行算法操作的指針必須是確定知道其指向數據類型大小的。 //例如: int *pint; pint++; //ANSI:正確 pint++的結果是使其增大sizeof(int)。
但是GNU則不這麼認定,它指定void *的算法操作與char *一致。因此下列語句在GNU編譯器中皆正確:
pvoid++; //GNU:正確 pvoid += 1; //GNU:正確 pvoid++的執行結果是其增大了1
在實際的程序設計中,為迎合ANSI標准,並提高程序的可移植性,我們可以這樣編寫實現同樣功能的代碼:
void * pvoid; (char *)pvoid++; //ANSI:正確;GNU:正確 (char *)pvoid += 1; //ANSI:錯誤;GNU:正確
GNU和ANSI還有一些區別,總體而言,GNU較ANSI更“開放”,提供了對更多語法的支持。但是我們在真實設計時,還是應該盡可能地迎合ANSI標准。 如果函數的參數可以是任意類型指針,那麼應聲明其參數為void *
注:void指針可以任意類型的數據,可以在程序中給我們帶來一些好處,函數中形為指針類型時,我們可以將其定義為void指針,這樣函數就可以接受任意類型的指針。如:
void的字面意思是“無類型”,void *則為“無類型指針”,void *可以指向任何類型的數據。
典型的如內存操作函數memcpy和memset的函數原型分別為:
void * memcpy(void *dest, const void *src, size_t len); void * memset ( void * buffer, int c, size_t num );
這樣,任何類型的指針都可以傳入memcpy和memset中,這也真實地體現了內存操作函數的意義,因為它操作的對象僅僅是一片內存,而不論這片內存是什麼類型(參見C語言實現泛型編程)。如果memcpy和memset的參數類型不是void *,而是char *,那才叫真的奇怪了!這樣的memcpy和memset明顯不是一個“純粹的,脫離低級趣味的”函數!void的出現只是為了一種抽象的需要,如果你正確地理解了面向對象中“抽象基類”的概念,也很容易理解void數據類型。正如不能給抽象基類定義一個實例,我們也不能定義一個void(讓我們類比的稱void為“抽象數據類型”)變量。