1.定義
首先void*中的void代表一個(gè)任意的數(shù)據(jù)類(lèi)型,"星號(hào)"代表一個(gè)指針,所以其就是一個(gè)任意數(shù)據(jù)類(lèi)型的指針。
對(duì)于指定數(shù)據(jù)類(lèi)型的指針如int* ,double*等,他們的sizeof都是4個(gè)字節(jié),因?yàn)槎际且粋€(gè)指針,只是指針指向的數(shù)據(jù)類(lèi)型不一致。
C語(yǔ)言是一個(gè)強(qiáng)類(lèi)型的語(yǔ)言, 那么他們之間有什么區(qū)別呢 ?前面一篇文章我們說(shuō)過(guò), 指針+1和-1是和指向數(shù)據(jù)類(lèi)型有關(guān)的 。
假設(shè)指針值為0x00000001,指針類(lèi)型為int類(lèi)型,整數(shù)為n,則計(jì)算出來(lái)的結(jié)果為0x00000001+ n乘以4,這里的4是因?yàn)橹羔橆?lèi)型為int,如果是double,則為0x00000001+ n乘以8. 所以我們使用double類(lèi)型指針+1,地址移動(dòng)了8位,而用int指針+1,地址移動(dòng)了4位,就是這個(gè)道理。
這說(shuō)起來(lái)其實(shí)和數(shù)據(jù)內(nèi)存對(duì)齊有關(guān)。你可以把這里的4和8理解為跳躍力。
那對(duì)于void*呢? 其實(shí)就是一個(gè)未指定跳躍力的指針 。
那void*的跳躍力又什么時(shí)候指定?在需要使用的時(shí)候指定就可以了,好處: 可以實(shí)現(xiàn)泛型編程,節(jié)省代碼 。
2.void*使用場(chǎng)景
2.1:當(dāng)函數(shù)傳參時(shí)不確定數(shù)據(jù)類(lèi)型時(shí)或者支持多種數(shù)據(jù)類(lèi)型傳遞時(shí)。
代碼如下:
void say(int type,void* pArgs) {
switch (type)
{
case 0:
{
double* d = (double*)pArgs;
break;
}
case 1:
{
int* i = (int*)pArgs;
break;
}
}
}
該函數(shù)使用一個(gè)type來(lái)表示當(dāng)前參數(shù)void*的類(lèi)型,內(nèi)部通過(guò)type判斷轉(zhuǎn)換的類(lèi)型。
2.2:函數(shù)返回值不需要考慮類(lèi)型,只關(guān)心返回的大小。
如malloc函數(shù):
原型:
void* malloc(size_t size)
代碼使用:
int* a = nullptr;
double* b = nullptr;
b = (double*)malloc(sizeof(double));
a = (int*)malloc(sizeof(double));
可以看到malloc返回值類(lèi)型為void*,其只返回分配內(nèi)存的大小,不關(guān)心分配后的內(nèi)存你是使用int還是double類(lèi)型進(jìn)行劃分.
注意:函數(shù)外部在接收到void*格式的返回值時(shí),需要強(qiáng)轉(zhuǎn)為自己的數(shù)據(jù)類(lèi)型才能使用。
3.void*使用中的注意點(diǎn):
1.使用賦值運(yùn)算符“=”時(shí),void*只能作為左值不能作為右值。
void*作為一個(gè)未指定數(shù)據(jù)類(lèi)型的指針,可以指向任何一個(gè)數(shù)據(jù)類(lèi)型的指針,但是有數(shù)據(jù)類(lèi)型的指針,不能指向一個(gè)void* 的指針。
代碼如下:
int i = 5;
int* pi = &i;
void* pv = pi;
int* pi1 = pv;//編譯錯(cuò)誤,void*類(lèi)型的指針不能初始化為指定類(lèi)型的指針
這其實(shí)是可以理解的:
假設(shè)void*指定了一個(gè)非int類(lèi)型的數(shù)據(jù),如果此時(shí)賦值給int*類(lèi)型的指針,則會(huì)發(fā)生嚴(yán)重的bug**。
2.void*類(lèi)型必須強(qiáng)轉(zhuǎn)為指定類(lèi)型的數(shù)據(jù)才能使用。
void 在未指定類(lèi)型的情況下,是不能直接使用的, 只有在轉(zhuǎn)換為顯示類(lèi)型后才能使用 。 *
代碼如下:
int i = 5;
int* pi = &i;
void* pv = pi;
//cout << *pv << endl;//表達(dá)式必須是指向完整對(duì)象類(lèi)型的指針
cout << *(int*)pv << endl;
代碼中可以看出在未強(qiáng)轉(zhuǎn)為顯示類(lèi)型前,使用void*會(huì)報(bào)表達(dá)式必須是指向完整對(duì)象類(lèi)型的指針.
說(shuō)明void*一定要強(qiáng)轉(zhuǎn)后才能使用.
沒(méi)有強(qiáng)轉(zhuǎn)的void*是沒(méi)有意義的。
那可能有同學(xué)要問(wèn)了,假設(shè)我們并不知道當(dāng)前void*數(shù)據(jù)類(lèi)型,強(qiáng)轉(zhuǎn)錯(cuò)誤了會(huì)發(fā)生什么事。
int i = 5;
int* pi = &i;
void* pv = pi;
cout<<"(int*)pv:" << (int*)pv << endl;
cout<<"(double*)pv:" << (double*)pv << endl;
cout <<"*(int*)pv:" << *(int*)pv << endl;
cout <<"*(double*)pv:" << *(double*)pv << endl;
運(yùn)行結(jié)果:
(int*)pv:0043F724
(double*)pv:0043F724
*(int*)pv:5
*(double*)pv:-9.25596e+61
此時(shí)可以看到雖然pv強(qiáng)轉(zhuǎn)為double后,指針指向的地址和強(qiáng)轉(zhuǎn)為int類(lèi)型指針指向地址是一樣的都是0x0043F724。但是取值后就發(fā)生了異常,因?yàn)閐ouble占用的是8個(gè)字節(jié),所以取值的是后8位字節(jié),所以取到的是一個(gè)錯(cuò)誤的值,實(shí)際只有4個(gè)字節(jié)是有效的。
我們?cè)谑褂胿oid*強(qiáng)轉(zhuǎn)的時(shí)候一定要注意這點(diǎn) 、
3.C++中使用(void*)0表示空指針。
在C語(yǔ)言中空指針定義方式:
#define NULL ((void*)0)
在C++語(yǔ)言中:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void*)0)
#endif
#endif
可以看到在C語(yǔ)言中NULL代表(void*)0,而在C++中NULL代表的是0,使用nullptr來(lái)表示(void*)0空指針,
所以在C++中推薦使用新標(biāo)準(zhǔn)的nullptr來(lái)初始化一個(gè)空指針。
4.當(dāng)void*作為函數(shù)的參數(shù)類(lèi)型或者返回值類(lèi)型時(shí),說(shuō)明該函數(shù)可以接收或者返回任意類(lèi)型的指針。
代碼如下:
void* _say(void* pArgs) {
return pArgs;
}
int main()
{
int _a = 5;
float f = 10.8;
int* _pi = &_a;
float* pf = &f;
cout << *(int*)_say(_pi) << endl;
cout << *(float*)_say(pf) << endl;
}
運(yùn)行結(jié)果:
5
10.8
代碼中可以看出參數(shù)void* pArgs可以使用任意類(lèi)型的實(shí)參,返回值也可以返回任意類(lèi)型的指針,但是最終需要轉(zhuǎn)換為具體類(lèi)型才能使用。
void*在C++中的作用其實(shí)就是為了實(shí)現(xiàn)泛型編程,和Java中使用Object來(lái)表示是一樣的,所以又稱(chēng)為通用指針和泛指針,不過(guò) C++中大部分情況下會(huì)使用模板編程來(lái)實(shí)現(xiàn)泛型 。
上面_say函數(shù)代碼可以使用下面模板函數(shù)代替:
T _say(T t) {
return t;
}
區(qū)別在于: 模板編程不需要將強(qiáng)制轉(zhuǎn)換為具體類(lèi)型 ,其使用方式如下:
int _a = 5;
float f = 10.8;
int* _pi = &_a;
float* pf = &f;
cout <<*_say(_pi) << endl;
cout << *_say(pf) << endl;
未強(qiáng)轉(zhuǎn)也可以直接得出結(jié)果,這是因?yàn)槟0寰幊虝?huì)在編譯器幫我們生成具體的函數(shù)。
調(diào)用了兩次_say會(huì)分別實(shí)現(xiàn):
float* _say(float* t) {
return t;
}
int* _say(int* t) {
return t;
}
所以運(yùn)行的時(shí)候是調(diào)用了不同的函數(shù),而使用void*的泛型只調(diào)用一個(gè)函數(shù)。
總結(jié)
-
1.void*是一個(gè)過(guò)渡型的指針狀態(tài),可以代表任意類(lèi)型的指針,取值的時(shí)候需要轉(zhuǎn)換為具體類(lèi)型才能取值。其是處于數(shù)據(jù)類(lèi)型頂端的狀態(tài):
-
2.void* 使用賦值運(yùn)算符“=”賦值時(shí),只能將具體類(lèi)型賦值給void星,不能將void*賦值給具體類(lèi)型。
-
3.void*一般作為參數(shù)或者返回值來(lái)實(shí)現(xiàn)泛型編程,但是C++中一般考慮使用模板編程來(lái)實(shí)現(xiàn)。
-
數(shù)據(jù)
+關(guān)注
關(guān)注
8文章
7250瀏覽量
91431 -
C++
+關(guān)注
關(guān)注
22文章
2118瀏覽量
74909 -
void
+關(guān)注
關(guān)注
0文章
23瀏覽量
10058
發(fā)布評(píng)論請(qǐng)先 登錄
C++中的結(jié)構(gòu)和類(lèi)
C語(yǔ)言和C++中那些不同的地方

C++筆記003:C++從一個(gè)小程序開(kāi)始
C++筆記004:C++類(lèi)通俗點(diǎn)說(shuō)—— C結(jié)構(gòu)體復(fù)習(xí)
C/C++程序員實(shí)用大全配套代碼
C++簡(jiǎn)介 ppt
C語(yǔ)言void及void指針深層探索
EE-128:C++中的DSP:從C++調(diào)用匯編類(lèi)成員函數(shù)

在C++中如何用虛函數(shù)實(shí)現(xiàn)多態(tài)
C++中struct和class的區(qū)別?
C++簡(jiǎn)史:C++是如何開(kāi)始的

C++中實(shí)現(xiàn)類(lèi)似instanceof的方法

評(píng)論