void的含義程序員
void即「無類型」,void *則爲「無類型指針」,能夠指向任何數據類型。
void指針使用規範
①void指針能夠指向任意類型的數據,亦便可用任意數據類型的指針對void指針賦值。例如:
int * pint;
void *pvoid;
pvoid = pint; /* 不過不能 pint= pvoid; */
若是要將pvoid賦給其餘類型指針,則須要強制類型轉換如:pint= (int *)pvoid;
②在ANSIC標準中,不容許對void指針進行算術運算如pvoid++或pvoid+=1等,而在GNU中則容許,由於在缺省狀況下,GNU認爲void *與char *同樣。sizeof(*pvoid )== sizeof( char).
void的做用
①對函數返回的限定。
②對函數參數的限定。
當函數不須要返回值時,必須使用void限定。例如: void func(int, int);
當函數不容許接受參數時,必須使用void限定。例如: int func(void)。
因爲void指針能夠指向任意類型的數據,亦便可用任意數據類型的指針對void指針賦值,所以還能夠用void指針來做爲函數形參,這樣函數就能夠接受任意數據類型的指針做爲參數。例如:
void * memcpy( void *dest, const void *src, size_t len );
void * memset( void * buffer, int c, size_t num);
------------------------------------------------------------------------------
算法
許多初學者對C/C++語言中的void及void指針類型不甚理解,所以在使用上出現了一些錯誤。本文將對void關鍵字的深入含義進行解說,並詳述void及void指針類型的使用方法與技巧。
編程
void的字面意思是「無類型」,void*則爲「無類型指針」,void*能夠指向任何類型的數據。void幾乎只有「註釋」和限制程序的做用,由於歷來沒有人會定義一個void變量,讓咱們試着來定義:
void a;
這行語句編譯時會出錯,提示「illegal use of type’void’」。不過,即便voida的編譯不會出錯,它也沒有任何實際意義。
void真正發揮的做用在於:
(1)對函數返回的限定;
(2)對函數參數的限定。
衆所周知,若是指針p1和p2的類型相同,那麼咱們能夠直接在p1和p2間互相賦值;若是p1和p2指向不一樣的數據類型,則必須使用強制類型轉換運算符把賦值運算符右邊的指針類型轉換爲左邊指針的類型。
例如:
float * p1;
in t* p2;
p1 = p2;
其中p1 = p2語句會編譯出錯,提示「’=’:cannotconvertfrom’int *’to’float *’」,必須改成:
p1=(float*)p2;
而void*則不一樣,任何類型的指針均可以直接賦值給它,無需進行強制類型轉換:
void * p1;
int * p2;
p1 = p2;
但這並不意味着,void*也能夠無需強制類型轉換地賦給其它類型的指針。由於「無類型」能夠包容「有類型」,而「有類型」則不能包容「無類型」。道理很簡單,咱們能夠說「男人和女人都是人」,但不能說「人是男人」或者「人是女人」。下面的語句編譯出錯:
void * p1;
int * p2;
p2 = p1;安全
提示「’=’:cannotconvertfrom’void*’to’int*’」。
函數
下面給出void關鍵字的使用規則:
規則一若是函數沒有返回值,那麼應聲明爲void類型
在C語言中,凡不加返回值類型限定的函數,就會被編譯器做爲返回整型值處理。可是許多程序員卻誤覺得其爲void類型。例如:
add(inta,intb)
{
return a+b;
}
int main(int argc,char * argv[])
{
printf(/"2+3=%d/",add(2,3));
}
程序運行的結果爲輸出:
2+3=5
這說明不加返回值說明的函數的確爲int函數。
林銳博士《高質量C/C++編程》中提到:「C++語言有很嚴格的類型安全檢查,不容許上述狀況(指函數不加類型聲明)發生」。但是編譯器並不必定這麼認定,譬如在VisualC++6.0中上述add函數的編譯無錯也無警告且運行正確,因此不能寄但願於編譯器會作嚴格的類型檢查。
所以,爲了不混亂,咱們在編寫C/C++程序時,對於任何函數都必須一個不漏地指定其類型。若是函數沒有返回值,必定要聲明爲void類型。這既是程序良好可讀性的須要,也是編程規範性的要求。另外,加上void類型聲明後,也能夠發揮代碼的「自注釋」做用。代碼的「自注釋」即代碼能本身註釋本身。 [Page]
規則二若是函數無參數,那麼應聲明其參數爲void
在C++語言中聲明一個這樣的函數:
int function(void)
{
return1;
}
則進行下面的調用是不合法的:
function(2);
由於在C++中,函數參數爲void的意思是這個函數不接受任何參數。
咱們在TurboC2.0中編譯:
#include"stdio.h"
fun()
{
return1;
}
main()
{
printf(/"%d/",fun(2));
getchar();
}
編譯正確且輸出1,這說明,在C語言中,能夠給無參數的函數傳送任意類型的參數,可是在C++編譯器中編譯一樣的代碼則會出錯。在C++中,不能向無參數的函數傳送任何參數,出錯提示「’fun’:functiondoesnottake1parameters」。
因此,不管在C仍是C++中,若函數不接受任何參數,必定要指明參數爲void。
規則三當心使用void指針類型
按照ANSI(AmericanNationalStandardsInstitute)標準,不能對void指針進行算法操做,即下列操做都是不合法的:
void * pvoid;
pvoid ++;//ANSI:錯誤
pvoid += 1;//ANSI:錯誤
ANSI標準之因此這樣認定,是由於它堅持:進行算法操做的指針必須是肯定知道其指向數據類型大小的。例如:
int * pint;
pint ++;//ANSI:正確
pint++的結果是使其增大sizeof(int)。
可是大名鼎鼎的GNU(GNU’sNotUnix的縮寫)則不這麼認定,它指定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*
典型的如內存操做函數memcpy和memset的函數原型分別爲:
void * memcpy(void*dest,constvoid*src,size_tlen);spa
void * memset(void*buffer,intc,size_tnum);設計
這樣,任何類型的指針均可以傳入memcpy和memset中,這也真實地體現了內存操做函數的意義,由於它操做的對象僅僅是一片內存,而不論這片內存是什麼類型。若是 memcpy和memset的參數類型不是void*,而是char*,那才叫真的奇怪了!這樣的memcpy和memset明顯不是一個「純粹的,脫離低級趣味的」函數!指針
下面的代碼執行正確:
//示例:memset接受任意類型指針
int intarray[100];[Page]
memset(intarray,0,100*sizeof(int));//將intarray清0
//示例:memcpy接受任意類型指針
int intarray1[100],intarray2[100];
memcpy(intarray1,intarray2,100*sizeof(int));//將intarray2拷貝給intarray1
有趣的是,memcpy和memset函數返回的也是void*類型,標準庫函數的編寫者是多麼地富有學問啊!
規則五void不能表明一個真實的變量
下面代碼都企圖讓void表明一個真實的變量,所以都是錯誤的代碼:
void a;//錯誤
function(void a);//錯誤
void體現了一種抽象,這個世界上的變量都是「有類型」的,譬如一我的不是男人就是女人(還有人妖?)。
void的出現只是爲了一種抽象的須要,若是你正確地理解了面向對象中「抽象基類」的概念,也很容易理解void數據類型。正如不能給抽象基類定義一個實例,咱們也不能定義一個void(讓咱們類比的稱void爲「抽象數據類型」)變量。對象