說到c就不得不提指標,而一提到指標,有乙個是比較特殊的,那就是void*
。void*
到底是怎樣的存在?
在說明void*
之前,先了解一下普通指標型別的含義。
#include int main(void)
; int *b = a;
char *c = (char*)&a[0];
printf("b+1:%dn",*(b+1));
printf("c+1:%dn",*(c+1));
return 0;}
上面的輸出結果為:
b+1:2019
c+1:3
對於上面的結果,也許你並不感到意外。如果你的疑問是為什麼不是2而是3,那麼建議你看看《談一談位元組序的問題》。同樣是指標型別,b和c有什麼區別?
乙個是指向整型的指標,乙個是指向char型的指標,當它們執行算術運算時,它們的步長就是對應型別占用空間大小。
即
b + 1 //移動sizeof(int)位元組
040302012019位元組0位元組1位元組2位元組3位元組4~7
↑指標移動4個位元組後,指向的就是2019了,解引用自然得到2019。而對於c
c + 1 //移動sizeof(char)位元組
它的指向如下:040302012019位元組0位元組1位元組2位元組3位元組4~7
解引用之後,自然得到3。
各種型別之間沒有本質區別,只是解釋記憶體中的資料方式不同。例如,對於int型指標b,解引用時,會解析4位元組,算術運算時,也是以該型別占用空間大小為單位,所以b+1,移動4位元組,解引用,處理4位元組內容,得到2019。對於char型指標c,解引用時,會解析1個位元組,算術運算時,也是以sizeof(char)為單位,所以c+1,移動一位元組,解引用,處理1位元組,得到03。所以像下面這樣的操作:
char a = ;
int *b = (int*)(a+2);
如果你試**引用b,即*b,就可能遇到無法預料的問題,因為將會訪問非法記憶體位置。a+2,移動sizeof(char)位元組,指向03,此時按照int型別指標解引用,由於int型別解引用會處理4位元組記憶體,但是後面已經沒有屬於陣列a的合法內容了,因此可能出錯。
正由於它們沒有本質區別,它們占用空間大小在同乙個程式中都是固定的,對於32位程式,占用4位元組空間,64位占用8位元組,而正因如此,64位程式理論能使用的記憶體是足夠大的,而32位程式理論上能使用的不過4g(2^(4*8bit)),再加上核心空間的使用,真正能用到的可能就3g左右。如果你的系統是64位的,那麼預設情況下,編譯出來的程式也是64位的。如果你想編譯為32位,可以使用-m32引數:
$ gcc -m32 -o main main.c
如何確定是多少位的程式:
$ readelf -h main
class: elf32
上面的elf32,表明了它是32位程式。或者可以看machine欄位:
machine: intel 80386
說回void*,前面說了,指標的型別不過是解釋資料的方式不同罷了,這樣的道理也可用於很多場合的強制型別轉換,例如將int型別指標轉換為char型指標,並不會改變記憶體的實際內容,只是修改了解釋方式而已。而void *是一種無型別指標,任何型別指標都可以轉為void*
,它無條件接受各種型別。而既然是無型別指標,那麼就不要嘗試做下面的事情:
由於不知道其解引用操作的記憶體大小,以及算術運算操作的大小,因此它的結果是未知的。
#include int main(void)
編譯警告如下:
warning: dereferencing 『void *』 pointer
既然如此,那麼void*有什麼用呢?實際上我們在很多介面中都會發現它們的引數型別都是void*,例如:
ssize_t read(int fd, void *buf, size_t count);
void *memcpy(void *dest, const void *src, size_t n);
為何要如此設計?因為對於這種通用型介面,你不知道使用者的資料型別是什麼,但是你必須能夠處理使用者的各種型別資料,因而會使用void*。void*能包容地接受各種型別的指標。也就是說,如果你期望介面能夠接受任何型別的引數,你可以使用void*型別。但是在具體使用的時候,你必須轉換為具體的指標型別。例如,你傳入介面的是int*,那麼你在使用的時候就應該按照int*使用。
使用void*需要特別注意的是,你必須清楚原始傳入的是什麼型別,然後轉換成對應型別。例如,你準備使用庫函式qsort進行排序:
void qsort(void *base,size_t nmemb,size_t size , int(*compar)(const void *,const void *));
它的第三個引數就是比較函式,它接受的引數都是const void*,如果你的比較物件是乙個結構體型別,那麼你自己在實現compar函式的時候,也必須是轉換為該結構體型別使用。舉個例子,你要實現學生資訊按照成績比較:
typedef struct student_tag
student_t;
int studentcompare(const void *stu1,const void *stu2)
在將其傳入studentcompare
函式後,必須轉換為其對應的型別進行處理。
void*很強大,但是一定要在合適的時候使用;同時強轉很逆天,但是一定要注意前後的型別是否真的能正確轉換。通俗地說void*:
高階指標話題-函式指標
談一談位元組序的問題
為何優先選用unique_ptr而不是裸指標?
函式引數的傳值和傳指標有什麼區別?
void型別及void指標型別
許多初學者對c c 語言中的void及void指標型別不甚理解,因此在使用上出現了一些錯誤。本文將對void關鍵字的深刻含義進行解說,並詳述void及void指標型別的使用方法與技巧。2.void的含義 void的字面意思是 無型別 void 則為 無型別指標 void 可以指向任何型別的資料。vo...
void及void指標型別
1.概述 許多初學者對c c 語言中的void及void指標型別不甚理解,因此在使用上出現了一些錯誤。本文將對void關鍵字的深刻含義進行解說,並 詳述void及void指標型別的使用方法與技巧。2.void的含義 void的字面意思是 無型別 void 則為 無型別指標 void 可以指向任何型別...
void型別及void指標
基於前面的一篇部落格模擬實現 memcpy 和 memmove 時用到的void 指標展開關於 void 和 void 指標的概述 1.void 相信大家不會陌生,經常定義無返回值的函式是用 void 定義,表示函式無需返回值 void fun void 2.void 的字面意思是 無型別 void...