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