為什么寫這篇文章呢? 為什么要弄這么個題目呢?
首先解釋為什么用這個題目。這一切都要從那天在QQ群中的討論說起,那天在群中,一個哥們問了一個關於(void)0 的問題。然后大家說到了
(void)0和(void*)0; 大家看看(void)0 和(void*)0 ,是不是一個像個平胸的小妹,一個像個豐韻的美女。
他問的的問題如下:
(void)0; //這個語句在C語句中可以執行嗎?
我想了一下這個語句應該沒有什么問題,在沒有用(void)0,去影響內存對象的情況下,應該是可以的。下面是我在FC 14中進行測試的結果。
[volcanol@volcanol c]$ ls a.out test.c [volcanol@volcanol c]$ cat test.c #include <stdio.h> int main(int argc,char** argv) { int test; test=0; (void)0; getchar(); return 0; } [volcanol@volcanol c]$ gcc -Wall test.c [volcanol@volcanol c]$
可以發現,編譯過程沒有警告和錯誤信息。
然后,我們就討論是否可以將(void)0 賦值給其他的對象,於是我就修改了一下,將其改成賦值。
[volcanol@volcanol c]$ cat -n test.c 1 #include <stdio.h> 2 3 int main(int argc,char** argv) 4 { 5 int test; 6 7 test=(void)0; 8 9 getchar(); 10 11 return 0; 12 } 13
編譯輸出信息:
[volcanol@volcanol c]$ gcc -Wall test.c test.c: 在函數‘main’中: test.c:7:8: 錯誤:void 值未如預期地被忽略
可以看出,void類型不能通過隱式類型轉換為int類型。
既然我們不能將void類型的值賦值,那么定義void類型的變量呢?
[volcanol@volcanol c]$ cat -n test.c 1 #include <stdio.h> 2 3 int main(int argc,char** argv) 4 { 5 void test; 6 7 test=(void)0; 8 9 getchar(); 10 11 return 0; 12 } 13
編譯器輸出信息如下所示:
[volcanol@volcanol c]$ gcc -Wall test.c test.c: 在函數‘main’中: test.c:5:10: 錯誤:變量或字段‘test’聲明為 void test.c:7:8: 錯誤:void 值未如預期地被忽略
可以發現,不能將變量定義為void類型。
下面我們來討論一下整個過程。
1、void類型
void表示為無類型,在 K&R C 里面有明確的說明。
因為不能在Linux下上傳圖片,所以就不上傳圖片里,可以到 K&R 第二版的 附錄 A.6.7 查看相關的內容。
K&R C里面明確的指出: void類型對象的值不能以任何方式使用,也不能顯式或者隱式的轉換為非空類型。void類型表示
一個不存在的值,任何對象轉換為void類型都將返回一個不存在的值,因此也就不能將void轉換為其他非空類型。
2、void類型的作用
【1】聲明函數不需要返回任何值
這個需要注意下面兩個函數定義的差別:
getx(); //函數1 void getx(); //函數2
注意:函數1和函數2是兩個不一樣的函數。
【2】聲明函數不需要傳遞任何
getx(); //函數1 getx(void); //函數2
注意:函數1和函數2 一樣。
【3】返回void對象的表達式語句,作用和空語句一樣。
(void)x; // 空對象表達式語句 ; //空語句
在編譯的時候,編譯優化后,這兩個語句都不產生任何實際代碼。
3、void* 類型
指向任何對象的指針都可以轉換為void* 類型,而且在轉換過程中不會丟失信息;一個被轉換為void* 類型的指針可以轉換為初始類型,
並且在轉換的過程中不會丟失信息。
char *p="Hello world"; char *pVoid = (void*)p;
下面是我的測試代碼:
[volcanol@volcanol c]$ cat -n test.c 1 #include <stdio.h> 2 3 int main(int argc,char** argv) 4 { 5 char *p="Hello world"; 6 7 char* pVoid=(void*)p; 8 9 puts(pVoid); 10 11 getchar(); 12 13 return 0; 14 } 15 [volcanol@volcanol c]$ gcc -Wall test.c [volcanol@volcanol c]$ ./a.out Hello world
可以看到,這樣的結果符合原本的定義。 分析代碼中的第7行語句, 首先將p指針顯式的轉換為(void*); 然后再將空類型指針(void*)p隱式的轉換為char*, 可以看到
這個過程沒有丟失信息。
這里我們可以看到: 挺拔的void*可以任意的和別人交往,而平胸的void成了嫁不出去的姑娘。
4、關於void* 變量和常量
在C語言庫中,預定義了一個常量:NULL; 通常是如下定義的:
#define NULL ((void*)0)
這個常量可以賦值給任意類型的指針。例如:
char* p=NULL;
這個語句在編譯時不會出錯,當然如果程序中不再對p進行再賦值,這個指針p通常也不能進行操作,例如此時不能用於puts(p);
這里還需要說一個特別的事情,就是關於0這個常量在C 語言中的應用,這個在獲取結構體變量的成員offset地址非常有用。
[volcanol@volcanol c]$ cat -n test.c 1 #include <stdio.h> 2 3 struct testType 4 { 5 int x; 6 char y; 7 struct testType *z; 8 }; 9 10 int main(int argc,char** argv) 11 { 12 13 int x=((size_t)&((struct testType*)0)->y); 14 15 printf("%d",x); 16 17 getchar(); 18 19 return 0; 20 } 21 [volcanol@volcanol c]$ gcc -Wall test.c [volcanol@volcanol c]$ ./a.out 4
這里內存地址0,可以引用為任意指針類型,這個內存地址是不允許應用程序引用的,這也就是如果將一個指針賦值為NULL后不能引用的原因。
關於void* 變量,在網絡上盛傳一篇文章: http://wenku.baidu.com/view/22c4b8d86f1aff00bed51edc.html,
這篇文章中對void* 指針某些描述,實際上是用某個平台的某個編譯的特殊情況來描述的,存在一些偏頗之處,例如文章中說
但這並不意味着,void *也可以無需強制類型轉換地賦給其它類型的指針。因為“無類型”可以包容“有類型”,而“有類型”則不能包容“無類型”。道理很簡單,
我們可以說“男人和女人都是人”,但不能說“人是男人”或者“人是女人”。下面的語句編譯出錯:
而且和多人還對里面的說法深信不疑, http://www.cnblogs.com/ComputerG/archive/2012/02/22/2363165.html
下面的這個鏈接對void* 指針的描述比較詳細: http://learn.akae.cn/media/ch23s01.html
我們可以看下面的代碼和執行情況:
[volcanol@volcanol c]$ cat -n test.c 1 #include <stdio.h> 2 3 int main(int argc,char** argv) 4 { 5 char *p="Hello world"; 6 7 void* pVoid=(void*)p; 8 9 puts(pVoid); 10 11 getchar(); 12 13 return 0; 14 } 15 [volcanol@volcanol c]$ gcc -Wall test.c [volcanol@volcanol c]$ ./a.out Hello world
實際上: void* 指針也是一個指針,具有普通指針的某些特性,例如 sizeof(void *)具有和sizeof(char *)一樣的大小,也就是說,void* 變量同樣占用4個字節(32位系統),這個
內存位置,同樣可以存儲一個內存地址。而設置void*的一個目的就是無需進行顯式地址轉換就可以將void* 指針轉換為其他任意指針, 因為最初的時候,是用char* 做這個功能的。
這樣每一次進行轉換的時候,都必須將char* 指針進行強制類型轉換。
這個地方,就不再細說了,在C語言中,(void)0注定有平胸姑娘的尷尬,成了嫁不出去的姑娘。
文章的內容,均為一孔之見,如果有不同意見,歡迎拍磚。 另外,前兩次有朋友說要我的電子書,這里我不太方便上傳,如果您需要電子書,可以加我QQ,我的QQ在博客園
中的個人信息里面添了,加的時候請說明是要電子書的,我會根據您的需要給你傳,要我上傳,我的書太多(達50G之多,很多種類的,不只是計算機的,有需要的都可以索要)。
