【C語言學習趣事】_32_平胸的尷尬,嫁不出去的姑娘


  為什么寫這篇文章呢? 為什么要弄這么個題目呢?

  首先解釋為什么用這個題目。這一切都要從那天在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之多,很多種類的,不只是計算機的,有需要的都可以索要)。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM