天天看點

void及void指針介紹【轉】

void類型指針(如void *p)所指向的資料類型不是确定的,或者說,可以是任何類型的。void類型指針中的資料不能通路,如果非要通路的話,可以通過顯式轉換将void類型指針轉換為與所指向的資料類型相符的類型。

1.任何類型的指針都可以顯式轉換為void類型,且不會丢失資料。

2.void類型指針可以通過顯式轉換為具有更小或相同存儲對齊限制的指針,但資料可能失真。所謂“相同存儲對齊限制”是指void類型指針所指的資料在記憶體中所占的長度與顯式轉換後的指針所指的資料在記憶體中所占的長度相等,比如p1所指的原資料在記憶體中占2個位元組,p2所指的資料在記憶體中也是占兩個資料。但應注意的是,隻有上面的這種轉換前後指針所指資料類型一緻的轉換才保持資料不失真,如果類型不一緻,即使具有相同存儲對齊限制,也有可能失真。

3.如果将void類型的指針轉換為具有更大存儲對齊限制的指針時,則會産生無效值。

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);

------------------------------------------------------------------------------      

1. 綜述

許多初學者對C/C++語言中的void及void指針類型不甚了解,是以在使用上出現了一些錯誤。本文将對void關鍵字的深刻含義進行解說,并詳述void及void指針類型的使用方法與技巧。

2.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*’”。

3.void的使用

  下面給出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);
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