void用法詳解

2021-06-07 15:46:49 字數 3989 閱讀 5871

void的字面意思是「無型別」,void *則為「無型別

指標 」,void *可以指向任何型別的資料。

void幾乎只有「注釋」和限制程式的作用,定義乙個void變數沒有意義,不妨試著定義:

void a;

這行語句編譯時會出錯,提示「illegal use of type 'void'」。不過,即使void a的編譯不會出錯,它也沒有任何實際意義。

void真正發揮的作用在於:

(1) 對函式返回的限定;

(2) 對函式引數的限定。

眾所周知,如果指標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 *p1;

int *p2;

p2 = p1;

提示「'=' : cannot convert from 'void *' to 'int *'」。

void的使用

下面給出void關鍵字的使用規則:

規則一如果函式沒有返回值,那麼應宣告為void型別

在c語言中,凡不加返回值型別限定的函式,就會被

編譯器作為返回整型值處理。但是許多程式設計師卻誤以為其為void型別。例如:

add ( int a, int b )

int main(int argc, char* argv)

程式執行的結果為輸出:

2 + 3 = 5

這說明不加返回值說明的函式的確為int函式。

林銳博士《高質量c/c++程式設計》中提到:「c++語言有很嚴格的型別安全檢查,不允許上述情況(指函式不加型別宣告)發生」。可是編譯器並不一定這麼認定,譬如在visual c++6.0中上述add函式的編譯無錯也無警告且執行正確,所以不能寄希望於編譯器會做嚴格的型別檢查。

因此,為了避免混亂,在編寫c/c++程式時,對於任何函式都必須乙個不漏地指定其型別。如果函式沒有返回值,一定要宣告為void型別。這既是程式良好可讀性的需要,也是程式設計規範性的要求。另外,加上void型別宣告後,也可以發揮**的「自注釋」作用。**的「自注釋」即**能自己注釋自己。

規則二如果函式無引數,那麼應宣告其引數為void。

在c++語言中宣告乙個這樣的函式:

int function(void)

則進行下面的呼叫是不合法的:

function(2);

因為在c++中,函式引數為void的意思是這個函式不接受任何引數。

在turbo c 2.0中編譯:

#include "

stdio.h"

fun()

main()

編譯正確且輸出1,這說明,在c語言中,可以給無引數的函式傳送任意型別的引數,但是在c++編譯器中編譯同樣的**則會出錯。在c++中,不能向無引數的函式傳送任何引數,出錯提示「'fun' : function does not take 1 parameters」。

所以,無論在c還是c++中,若函式不接受任何引數,一定要指明引數為void。

規則三小心使用void指標型別

按照ansi(american national standards institute)標準,不能對void指標進行演算法操作,即下列操作都是不合法的:

void * pvoid;

pvoid++; //ansi:錯誤

pvoid += 1; //ansi:錯誤

//ansi標準

之所以這樣認定,是因為它堅持:進行演算法操作的指標必須是確定知道其指向資料型別大小的。

//例如:

int *pint;

pint++; //ansi:正確

pint++的結果是使其增大sizeof(int)。( 在vc6.0上測試是sizeof(int)的倍數)

但是大名鼎鼎的gnu(gnu's not unix的縮寫)則不這麼認定,它指定void *的演算法操作與char *一致。

因此下列語句在gnu編譯器中皆正確:

pvoid++; //gnu:正確

pvoid += 1; //gnu:正確

pvoid++的執行結果是其增大了1。( 在vc6.0上測試是sizeof(int)的倍數)

在實際的程式設計中,為迎合ansi標準,並提高程式的可移植性,我們可以這樣編寫實現同樣功能的**:

void * pvoid;

(char *)pvoid++; //ansi:正確;gnu:正確

(char *)pvoid += 1; //ansi:錯誤;gnu:正確

gnu和ansi還有一些區別,總體而言,gnu較ansi更「開放」,提供了對更多語法的支援。但是我們在真實設計時,還是應該盡可能地迎合ansi標準。

規則四如果函式的引數可以是任意型別指標,那麼應宣告其引數為void *

典型的如記憶體操作函式memcpy和memset的函式原型分別為:

void * memcpy(void *dest, const void *src,

size_t

len);

void * memset ( void * buffer, int c, size_t num );

這樣,任何型別的指標都可以傳入memcpy和memset中,這也真實地體現了記憶體操作函式的意義,因為它操作的物件僅僅是一片記憶體,而不論這片記憶體是什麼型別。如果memcpy和memset的引數型別不是void *,而是char *,那才叫真的奇怪了!這樣的memcpy和memset明顯不是乙個「純粹的,脫離低階趣味的」函式!

下面的**執行正確:

//示例:memset接受任意型別指標

int intarray[100];

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為「

抽象資料型別

」)變數。

void 詳解和用法

用過memset或者memcpy的細心的人會發現,在gcc中編譯類似下面的 都不會有警告 int a int p a memset p,0,4 或者char c 4 char p c memset p,0,4 為什麼memset傳int 還是char 都沒有問題呢?我們知道不同型別間複製是要進行強制...

void指標 void 的用法

指標有兩個屬性 指向變數 物件的位址和長度 但是指標只儲存位址,長度則取決於指標的型別 編譯器根據指標的型別從指標指向的位址向後定址 指標型別不同則定址範圍也不同,比如 int 從指定位址向後尋找4位元組作為變數的儲存單元 double 從指定位址向後尋找8位元組作為變數的儲存單元 1.void指標...

void指標 void 的用法

指標有兩個屬性 指向變數 物件的位址和長度 但是指標只儲存位址,長度則取決於指標的型別 編譯器根據指標的型別從指標指向的位址向後定址 指標型別不同則定址範圍也不同,比如 int 從指定位址向後尋找4位元組作為變數的儲存單元 double 從指定位址向後尋找8位元組作為變數的儲存單元 1.void指標...