<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    JUST DO IT ~

    我只想當(dāng)個(gè)程序員

    [雜記](méi)非mfc項(xiàng)目 handle 替換 void *

     

    一般來(lái)說(shuō),如果你不是MFC工程,需要引用HANDLE的話(huà),
    最好自己將其類(lèi)型改為void*,比如,你本來(lái)變量的類(lèi)型為HANDLE,
    你把它改為void*就可以了。

     

    --------------------------------------------------------------------------------

     

     

     

     

    任何類(lèi)型的指針,以及0,都可以隱式的轉(zhuǎn)換為void*,但是由于void*不包含任何類(lèi)型信息(你想想void是什么意思:-) ),從void*到其它任何類(lèi)型都沒(méi)有隱式的轉(zhuǎn)換。所以只有強(qiáng)制轉(zhuǎn)換。強(qiáng)制轉(zhuǎn)換(我指的是brute force那樣的)有兩種方式,一是如樓上所說(shuō)的,舊式的C風(fēng)格的強(qiáng)制類(lèi)型轉(zhuǎn)換。由于強(qiáng)制類(lèi)型轉(zhuǎn)換經(jīng)常是很多隱蔽的錯(cuò)誤的根源,所以在C++中應(yīng)該盡量少的使用,使用的時(shí)候也應(yīng)該用新的C++風(fēng)格寫(xiě)法,e.g.
    int i = 3;;
    void *p = &i; //隱式轉(zhuǎn)換,自動(dòng)的
    int *p = reinterpret_cast<int*>(p);
    這樣寫(xiě)的意義在于使得即便發(fā)生了因強(qiáng)制類(lèi)型轉(zhuǎn)換而產(chǎn)生的錯(cuò)誤,也更容易找到。B.Stroustrup之所以把這個(gè)操作符設(shè)計(jì)的這么丑陋,就是要你盡量少用,用了也容易查出來(lái)。

     

     

     


    樓主說(shuō)的是將void *指針賦給其他指針吧,因?yàn)榭罩羔槢](méi)有指明類(lèi)型,只有一個(gè)地址而已,如果賦給int 等類(lèi)型的指針在VC中會(huì)出現(xiàn)“can't convert from 'void *' to 'int *'的編譯錯(cuò)誤!

    就像不同類(lèi)型的普通變量之間賦值一樣!
    所以要通過(guò)強(qiáng)制轉(zhuǎn)換指針類(lèi)型!

    下面是錢(qián)能教程中的一段代碼:

      int a=20;
      int *pr=&a;
      void *p=pr;        //ok:將整型指針值賦給空類(lèi)型指針
      pr=p;              //error:不能將空類(lèi)型指針賦給其他指針
      pr=(int *)p;       //ok: 顯式轉(zhuǎn)換被允許

     

    發(fā)表于: 2004-03-31 22:56:29
    <<C++Primer>>有一節(jié)介紹void*強(qiáng)制轉(zhuǎn)換的問(wèn)題,看了很久都沒(méi)看懂,哪衛(wèi)高手能詳細(xì)的講一下

     

     

     

     

     


    轉(zhuǎn))指針的引用(*&)與指針的指針(**) - [C,C++]
    版權(quán)聲明:轉(zhuǎn)載時(shí)請(qǐng)以超鏈接形式標(biāo)明文章原始出處和作者信息及本聲明
    http://hiaurora.blogbus.com/logs/23641245.html

    原文鏈接:http://www.programfan.com/article/2901.html

    在下列函數(shù)聲明中,為什么要同時(shí)使用*和&符號(hào)?以及什么場(chǎng)合使用這種聲明方式?

        void func1( MYCLASS *&pBuildingElement );

    論壇中經(jīng)常有人問(wèn)到這樣的問(wèn)題。本文試圖通過(guò)一些實(shí)際的指針使用經(jīng)驗(yàn)來(lái)解釋這個(gè)問(wèn)題。仔細(xì)看一下這種聲明方式,確實(shí)有點(diǎn)讓人迷惑。在某種意義上,"*"和"&"是意思相對(duì)的兩個(gè)東西,把它們放在一起有什么意義呢?。

        為了理解指針的這種做法,我們先復(fù)習(xí)一下C/C++編程中無(wú)所不在的指針概念。我們都知道MYCLASS*的意思:指向某個(gè)對(duì)象的指針,此對(duì)象的類(lèi)型為MYCLASS。

       Void func1(MYCLASS *pMyClass); // 例如:
       MYCLASS* p = new MYCLASS;
       func1(p);

        上面這段代碼的這種處理方法想必誰(shuí)都用過(guò),創(chuàng)建一個(gè)MYCLASS對(duì)象,然后將它傳入func1函數(shù)?,F(xiàn)在假設(shè)此函數(shù)要修改pMyClass:

      void func1(MYCLASS *pMyClass) {
          DoSomething(pMyClass);
          pMyClass = // 其它對(duì)象的指針
      }

        第二條語(yǔ)句在函數(shù)過(guò)程中只修改了pMyClass的值。并沒(méi)有修改調(diào)用者的變量p的值。如果p指向某個(gè)位于地址0x008a00的對(duì)象,當(dāng)func1返回時(shí),它仍然指向這個(gè)特定的對(duì)象。(除非func1有bug將堆弄亂了,完全有這種可能。)

      現(xiàn)在假設(shè)你想要在func1中修改p的值。這是你的權(quán)利。調(diào)用者傳入一個(gè)指針,然后函數(shù)給這個(gè)指針賦值。以往一般都是傳雙指針,即指針的指針,例如,    CMyClass**。

       MYCLASS* p = NULL;
       func1(&p);
       void func1(MYCLASS** pMyClass);
       { *pMyClass = new MYCLASS; …… }

    調(diào)用func1之后,p指向新的對(duì)象。在COM編程中,你到處都會(huì)碰到這樣的用法--例如在查詢(xún)對(duì)象接口的QueryInterface函數(shù)中:

       interface ISomeInterface {
           HRESULT QueryInterface(IID &iid, void** ppvObj);
          ……
       };
       LPSOMEINTERFACE p=NULL;
       pOb->QueryInterface(IID_SOMEINTERFACE, &p);

    此處,p是SOMEINTERFACE類(lèi)型的指針,所以&p便是指針的指針,在QueryInterface返回的時(shí)候,如果調(diào)用成功,則變量p包含一個(gè)指向新的接口的指針。

    如果你理解指針的指針,那么你肯定就理解指針引用,因?yàn)樗鼈兺耆且换厥隆H绻阆笙旅孢@樣聲明函數(shù):

       void func1(MYCLASS *&pMyClass)  {
           pMyClass = new MYCLASS;
       ……
       }

    其實(shí),它和前面所講得指針的指針例子是一碼事,只是語(yǔ)法有所不同。傳遞的時(shí)候不用傳p的地址&p,而是直接傳p本身:

    MYCLASS* p = NULL;
      func1(p);

    在調(diào)用之后,p指向一個(gè)新的對(duì)象。一般來(lái)講,引用的原理或多或少就象一個(gè)指針,從語(yǔ)法上看它就是一個(gè)普通變量。所以只要你碰到*&,就應(yīng)該想到**。也就是說(shuō)這個(gè)函數(shù)修改或可能修改調(diào)用者的指針,而調(diào)用者象普通變量一樣傳遞這個(gè)指針,不使用地址操作符&。

      至于說(shuō)什么場(chǎng)合要使用這種方法,我會(huì)說(shuō),極少。MFC在其集合類(lèi)中用到了它--例如,CObList,它是一個(gè)Cobjects指針列表。

        Class CObList : public Cobject {
             …… // 獲取/修改指定位置的元素
             Cobject*& GetAt(POSITION position);
             Cobject* GetAt(POSITION position) const;
        };

      這里有兩個(gè)GetAt函數(shù),功能都是獲取給定位置的元素。區(qū)別何在呢?

      區(qū)別在于一個(gè)讓你修改列表中的對(duì)象,另一個(gè)則不行。所以如果你寫(xiě)成下面這樣:

         Cobject* pObj = mylist.GetAt(pos);

    則pObj是列表中某個(gè)對(duì)象的指針,如果接著改變pObj的值: pObj = pSomeOtherObj;

      這并改變不了在位置pos處的對(duì)象地址,而僅僅是改變了變量pObj。但是,如果你寫(xiě)成下面這樣: Cobject*& rpObj = mylist.GetAt(pos);

      現(xiàn)在,rpObj是引用一個(gè)列表中的對(duì)象的指針,所以當(dāng)改變r(jià)pObj時(shí),也會(huì)改變列表中位置pos處的對(duì)象地址--換句話(huà)說(shuō),替代了這個(gè)對(duì)象。這就是為什么CObList會(huì)有兩個(gè)GetAt函數(shù)的緣故。一個(gè)可以修改指針的值,另一個(gè)則不能。注意我在此說(shuō)的是指針,不是對(duì)象本身。這兩個(gè)函數(shù)都可以修改對(duì)象,但只有*&版本可以替代對(duì)象。

      在C/C++中引用是很重要的,同時(shí)也是高效的處理手段。所以要想成為C/C++高手,對(duì)引用的概念沒(méi)有透徹的理解和熟練的應(yīng)用是不行的。 ################################################################ 注記:函數(shù)的參數(shù)為指針,如func(MyClass* p). 傳參時(shí), MyClass* my; func(my); 相當(dāng)于傳的指針的拷貝,是一個(gè)臨時(shí)值。因此,在函數(shù)體內(nèi)部,你可以更改p指向的MyClass的屬性,但是你不能給p賦值,就向上面所說(shuō),p = new MyClass();或者*p = 。。。因?yàn)椋词鼓氵@樣做了,出了函數(shù)的作用的作用域,my仍然指向原來(lái)的地址。而你在函數(shù)題內(nèi)部,改變的是臨時(shí)變量指針p的地址。所以,不能對(duì)p進(jìn)行賦值等操作。 附圖說(shuō)明:


    歷史上的今天:

     

     

     

    c++的類(lèi)型轉(zhuǎn)換:void *無(wú)法被轉(zhuǎn)為struct node*,而在C下卻可以,又說(shuō)C++兼容C ? 我定義個(gè)指向結(jié)構(gòu)體的指針,
    struct node*p;
    void *fun();
    p=fun();
    C下完美通過(guò),C++出錯(cuò)
    那個(gè),現(xiàn)在的說(shuō)法是:C是C,C++是C++。
    要強(qiáng)制轉(zhuǎn)換。。。

    不知道是不是vc,一般來(lái)說(shuō)c++校驗(yàn)比較嚴(yán)格,而c則給程序員較大的自由度(不過(guò)風(fēng)險(xiǎn)也相應(yīng)存在)
    p= (struct node*)fun();
    你說(shuō)對(duì)了,在C下面void*可以隱式轉(zhuǎn)換為左值指針類(lèi)型,而C++必須加強(qiáng)制轉(zhuǎn)換


    再說(shuō) c99 也沒(méi)完全兼容以前的c

    又何必要求c++ 就得完全兼容 c


    引用 5 樓 thefirstz 的回復(fù):
    你說(shuō)對(duì)了,在C下面void*可以隱式轉(zhuǎn)換為左值指針類(lèi)型,而C++必須加強(qiáng)制轉(zhuǎn)換

    學(xué)習(xí)了~~~~~~~~~~~~~~~~~~


    通常不涉及對(duì)象時(shí),指針強(qiáng)轉(zhuǎn),沒(méi)有副作用

    *********************************************
    p=static_cast<struct node*>(fun());

    ***********************************************
    p=(TYPE*)fun();
    再試試

    *****************************************************

    這點(diǎn)應(yīng)該算是C++相對(duì)C的一個(gè)優(yōu)點(diǎn)吧
    較強(qiáng)的類(lèi)型檢查
    ******************************************
    應(yīng)該是void* 無(wú)條件接受任何類(lèi)型的指針吧,反之不行

     

     

     

     


    C++中,為什么必須用造型來(lái)轉(zhuǎn)換*void

    上一節(jié) 下一節(jié) 返回目錄編輯/糾錯(cuò)/意見(jiàn)關(guān)注(146)更新:2012-12-31
    分享到0
    在C 語(yǔ)言中,你可以隱式地將*void 轉(zhuǎn)換為*T。這是不安全的??紤]一下:
    #include<stdio.h>
    int main(){
        char i = 0;
        char j = 0;
        char* p = &i;
        void* q = p;
        int* pp = q; /* 不安全的,在C 中可以,C++不行 */
        printf("%d %d\n",i,j);
        *pp = -1; /* 覆蓋了從i 開(kāi)始的內(nèi)存 */
        printf("%d %d\n",i,j);
    }

    使用一個(gè)并不指向T 類(lèi)型的T*將是一場(chǎng)災(zāi)難。因此,在C++中,如果從一個(gè)void*得到一個(gè)T*,你必須進(jìn)行顯式轉(zhuǎn)換。舉例來(lái)說(shuō),要得到上列程序的這個(gè)令人別扭的效果,你可以這樣寫(xiě):
        int* pp = (int*)q;

    或者使用一個(gè)新的類(lèi)型造型,以使這種沒(méi)有檢查的類(lèi)型轉(zhuǎn)換操作變得更加清晰:
        int* pp = static_cast<int*>(q);

    造型被最好地避免了。

    在C 語(yǔ)言中,這種不安全的轉(zhuǎn)換最常見(jiàn)的應(yīng)用之一,是將malloc()的結(jié)果賦予一個(gè)合適的指針。例如:
        int* p = malloc(sizeof(int));

    在C++中,使用類(lèi)型安全的new 操作符:
        int* p = new int;

    附帶地,new 操作符還提供了勝過(guò)malloc()的新特性:
        new 不會(huì)偶然分配錯(cuò)誤的內(nèi)存數(shù)量;
        new 會(huì)隱式地檢查內(nèi)存耗盡情況,而且
        new 提供了初始化。

    舉例:
        typedef std::complex<double> cmplx;
        /* C 風(fēng)格: */
        cmplx* p = (cmplx*)malloc(sizeof(int)); /* 錯(cuò)誤:類(lèi)型不正確 */
        /* 忘記測(cè)試p==0 */
        if (*p == 7) { /* ... */ } /* 糟糕,忘記了初始化*p */
        // C++風(fēng)格:
        cmplx* q = new cmplx(1,2); // 如果內(nèi)存耗盡,將拋出一個(gè)bad_alloc 異常
        if (*q == 7) { /* ... */ }

     

     

     

     

     

     

     

     

     

     

     

    C語(yǔ)言位運(yùn)算 - [C,C++]
    版權(quán)聲明:轉(zhuǎn)載時(shí)請(qǐng)以超鏈接形式標(biāo)明文章原始出處和作者信息及本聲明
    http://hiaurora.blogbus.com/logs/29800492.html

    前面介紹的各種運(yùn)算都是以字節(jié)作為最基本位進(jìn)行的。 但在很多系統(tǒng)程序中常要求在位(bit)一級(jí)進(jìn)行運(yùn)算或處理。C語(yǔ)言提供了位運(yùn)算的功能, 這使得C語(yǔ)言也能像匯編語(yǔ)言一樣用來(lái)編寫(xiě)系統(tǒng)程序。
    一、位運(yùn)算符C語(yǔ)言提供了六種位運(yùn)算符:
    & 按位與
    | 按位或
    ^ 按位異或
    ~ 取反
    << 左移
    >> 右移

    1. 按位與運(yùn)算 按位與運(yùn)算符"&"是雙目運(yùn)算符。其功能是參與運(yùn)算的兩數(shù)各對(duì)應(yīng)的二進(jìn)位相與。只有對(duì)應(yīng)的兩個(gè)二進(jìn)位均為1時(shí),結(jié)果位才為1 ,否則為0。參與運(yùn)算的數(shù)以補(bǔ)碼方式出現(xiàn)。
    例如:9&5可寫(xiě)算式如下: 00001001 (9的二進(jìn)制補(bǔ)碼)&00000101 (5的二進(jìn)制補(bǔ)碼) 00000001 (1的二進(jìn)制補(bǔ)碼)可見(jiàn)9&5=1。

    按位與運(yùn)算通常用來(lái)對(duì)某些位清0或保留某些位。例如把a(bǔ) 的高八位清 0 , 保留低八位, 可作 a&255 運(yùn)算 ( 255 的二進(jìn)制數(shù)為0000000011111111)。
    main(){
    int a=9,b=5,c;
    c=a&b;
    printf("a=%d\nb=%d\nc=%d\n",a,b,c);
    }

    2. 按位或運(yùn)算 按位或運(yùn)算符“|”是雙目運(yùn)算符。其功能是參與運(yùn)算的兩數(shù)各對(duì)應(yīng)的二進(jìn)位相或。只要對(duì)應(yīng)的二個(gè)二進(jìn)位有一個(gè)為1時(shí),結(jié)果位就為1。參與運(yùn)算的兩個(gè)數(shù)均以補(bǔ)碼出現(xiàn)。
    例如:9|5可寫(xiě)算式如下: 00001001|00000101
    00001101 (十進(jìn)制為13)可見(jiàn)9|5=13
    main(){
    int a=9,b=5,c;
    c=a|b;
    printf("a=%d\nb=%d\nc=%d\n",a,b,c);
    }

    3. 按位異或運(yùn)算 按位異或運(yùn)算符“^”是雙目運(yùn)算符。其功能是參與運(yùn)算的兩數(shù)各對(duì)應(yīng)的二進(jìn)位相異或,當(dāng)兩對(duì)應(yīng)的二進(jìn)位相異時(shí),結(jié)果為1。參與運(yùn)算數(shù)仍以補(bǔ)碼出現(xiàn),例如9^5可寫(xiě)成算式如下: 00001001^00000101 00001100 (十進(jìn)制為12)
    main(){
    int a=9;
    a=a^15;
    printf("a=%d\n",a);
    }

    4. 求反運(yùn)算 求反運(yùn)算符~為單目運(yùn)算符,具有右結(jié)合性。 其功能是對(duì)參與運(yùn)算的數(shù)的各二進(jìn)位按位求反。例如~9的運(yùn)算為: ~(0000000000001001)結(jié)果為:1111111111110110

    5. 左移運(yùn)算 左移運(yùn)算符“<<”是雙目運(yùn)算符。其功能把“<< ”左邊的運(yùn)算數(shù)的各二進(jìn)位全部左移若干位,由“<<”右邊的數(shù)指定移動(dòng)的位數(shù),
    高位丟棄,低位補(bǔ)0。例如: a<<4 指把a(bǔ)的各二進(jìn)位向左移動(dòng)4位。如a=00000011(十進(jìn)制3),左移4位后為00110000(十進(jìn)制48)。6. 右移運(yùn)算 右移運(yùn)算符“>>”是雙目運(yùn)算符。其功能是把“>> ”左邊的運(yùn)算數(shù)的各二進(jìn)位全部右移若干位,“>>”右邊的數(shù)指定移動(dòng)的位數(shù)。
    例如:設(shè) a=15,a>>2 表示把000001111右移為00000011(十進(jìn)制3)。 應(yīng)該說(shuō)明的是,對(duì)于有符號(hào)數(shù),在右移時(shí),符號(hào)位將隨同移動(dòng)。當(dāng)為正數(shù)時(shí), 最高位補(bǔ)0,而為負(fù)數(shù)時(shí),符號(hào)位為1,最高位是補(bǔ)0或是補(bǔ)1 取決于編譯系統(tǒng)的規(guī)定。Turbo C和很多系統(tǒng)規(guī)定為補(bǔ)1。
    main(){
    unsigned a,b;
    printf("input a number: ");
    scanf("%d",&a);
    b=a>>5;
    b=b&15;
    printf("a=%d\tb=%d\n",a,b);
    }
    請(qǐng)?jiān)倏匆焕?
    main(){
    char a='a',b='b';
    int p,c,d;
    p=a;
    p=(p<<8)|b;
    d=p&0xff;
    c=(p&0xff00)>>8;
    printf("a=%d\nb=%d\nc=%d\nd=%d\n",a,b,c,d);
    }

    位域

    有些信息在存儲(chǔ)時(shí),并不需要占用一個(gè)完整的字節(jié), 而只需占幾個(gè)或一個(gè)二進(jìn)制位。例如在存放一個(gè)開(kāi)關(guān)量時(shí),只有0和1 兩種狀態(tài), 用一位二進(jìn)位即可。為了節(jié)省存儲(chǔ)空間,并使處理簡(jiǎn)便,C語(yǔ)言又提供了一種數(shù)據(jù)結(jié)構(gòu),稱(chēng)為“位域”或“位段”。所謂“位域”是把一個(gè)字節(jié)中的二進(jìn)位劃分為幾個(gè)不同的區(qū)域, 并說(shuō)明每個(gè)區(qū)域的位數(shù)。每個(gè)域有一個(gè)域名,允許在程序中按域名進(jìn)行操作。 這樣就可以把幾個(gè)不同的對(duì)象用一個(gè)字節(jié)的二進(jìn)制位域來(lái)表示。一、位域的定義和位域變量的說(shuō)明位域定義與結(jié)構(gòu)定義相仿,其形式為:
    struct 位域結(jié)構(gòu)名
    { 位域列表 };
    其中位域列表的形式為: 類(lèi)型說(shuō)明符 位域名:位域長(zhǎng)度
    例如:
    struct bs
    {
    int a:8;
    int b:2;
    int c:6;
    };
    位域變量的說(shuō)明與結(jié)構(gòu)變量說(shuō)明的方式相同。 可采用先定義后說(shuō)明,同時(shí)定義說(shuō)明或者直接說(shuō)明這三種方式。例如:
    struct bs
    {
    int a:8;
    int b:2;
    int c:6;
    }data;
    說(shuō)明data為bs變量,共占兩個(gè)字節(jié)。其中位域a占8位,位域b占2位,位域c占6位。對(duì)于位域的定義尚有以下幾點(diǎn)說(shuō)明:

    1. 一個(gè)位域必須存儲(chǔ)在同一個(gè)字節(jié)中,不能跨兩個(gè)字節(jié)。如一個(gè)字節(jié)所??臻g不夠存放另一位域時(shí),應(yīng)從下一單元起存放該位域。也可以有意使某位域從下一單元開(kāi)始。例如:
    struct bs
    {
    unsigned a:4
    unsigned :0 /*空域*/
    unsigned b:4 /*從下一單元開(kāi)始存放*/
    unsigned c:4
    }
    在這個(gè)位域定義中,a占第一字節(jié)的4位,后4位填0表示不使用,b從第二字節(jié)開(kāi)始,占用4位,c占用4位。

    2. 由于位域不允許跨兩個(gè)字節(jié),因此位域的長(zhǎng)度不能大于一個(gè)字節(jié)的長(zhǎng)度,也就是說(shuō)不能超過(guò)8位二進(jìn)位。

    3. 位域可以無(wú)位域名,這時(shí)它只用來(lái)作填充或調(diào)整位置。無(wú)名的位域是不能使用的。例如:
    struct k
    {
    int a:1
    int :2 /*該2位不能使用*/
    int b:3
    int c:2
    };
    從以上分析可以看出,位域在本質(zhì)上就是一種結(jié)構(gòu)類(lèi)型, 不過(guò)其成員是按二進(jìn)位分配的。

    二、位域的使用位域的使用和結(jié)構(gòu)成員的使用相同,其一般形式為: 位域變量名·位域名 位域允許用各種格式輸出。
    main(){
    struct bs
    {
    unsigned a:1;
    unsigned b:3;
    unsigned c:4;
    } bit,*pbit;
    bit.a=1;
    bit.b=7;
    bit.c=15;
    printf("%d,%d,%d\n",bit.a,bit.b,bit.c);
    pbit=&bit;
    pbit->a=0;
    pbit->b&=3;
    pbit->c|=1;
    printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);
    }
    上例程序中定義了位域結(jié)構(gòu)bs,三個(gè)位域?yàn)閍,b,c。說(shuō)明了bs類(lèi)型的變量bit和指向bs類(lèi)型的指針變量pbit。這表示位域也是可以使用指針的。
    程序的9、10、11三行分別給三個(gè)位域賦值。( 應(yīng)注意賦值不能超過(guò)該位域的允許范圍)程序第12行以整型量格式輸出三個(gè)域的內(nèi)容。第13行把位域變量bit的地址送給指針變量pbit。第14行用指針?lè)绞浇o位域a重新賦值,賦為0。第15行使用了復(fù)合的位運(yùn)算符"&=", 該行相當(dāng)于: pbit->b=pbit->b&3位域b中原有值為7,與3作按位與運(yùn)算的結(jié)果為3(111&011=011,十進(jìn)制值為3)。同樣,程序第16行中使用了復(fù)合位運(yùn)算"|=", 相當(dāng)于: pbit->c=pbit->c|1其結(jié)果為15。程序第17行用指針?lè)绞捷敵隽诉@三個(gè)域的值。

    浮點(diǎn)數(shù)的存儲(chǔ)格式:

    浮點(diǎn)數(shù)的存儲(chǔ)格式是符號(hào)+階碼(定點(diǎn)整數(shù))+尾數(shù)(定點(diǎn)小數(shù))
    SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM
    即1位符號(hào)位(0為正,1為負(fù)),8位指數(shù)位,23位尾數(shù)位
    浮點(diǎn)數(shù)存儲(chǔ)前先轉(zhuǎn)化成2的k次方形式,即:
    f = A1*2^k + A2*2^(k-1) + ... + Ak +... +An*2^(-m) (Ai = {0, 1}, A1 = 1)
    如5.5=2^2 + 2^0 + 2^(-1)
    其中的k就是指數(shù),加127后組成8位指數(shù)位
    5.5的指數(shù)位就是2+127 = 129 = 10000001
    A2A3.....An就是尾數(shù)位,不足23位后補(bǔ)0
    所以5.5 = 01000000101000000000000000000000 = 40A00000
    所以,對(duì)浮點(diǎn)數(shù)*2、/2只要對(duì)8位符號(hào)位+、- 即可,但不是左移、右移

    關(guān)于unsigned int 和 int 的在位運(yùn)算上的不同,下面有個(gè)CU上的例子描述的很清楚:

    [問(wèn)題]:這個(gè)函數(shù)有什么問(wèn)題嗎?

    /////////////////////////////////////////////////
    /**
    * 本函數(shù)將兩個(gè)16比特位的值連結(jié)成為一個(gè)32比特位的值。
    * 參數(shù):sHighBits 高16位
    * sLowBits 低16位
    * 返回:32位值
    **/
    long CatenateBits16(short sHighBits, short sLowBits)
    {
    long lResult = 0; /* 32位值的臨時(shí)變量*/

    /* 將第一個(gè)16位值放入32位值的高16位 */
    lResult = sHighBits;
    lResult <<= 16;

    /* 清除32位值的低16位 */
    lResult &= 0xFFFF0000;

    /* 將第二個(gè)16位值放入32位值的低16位 */
    lResult |= (long)sLowBits;

    return lResult;
    }
    /////////////////////////////////////////////////


    [問(wèn)題的發(fā)現(xiàn)]:

    我們先看如下測(cè)試代碼:

    /////////////////////////////////////////////////
    int main()
    {
    short sHighBits1 = 0x7fff;
    short sHighBits2 = 0x8f12;
    unsigned short usHighBits3 = 0xff12;
    short sLowBits1 = 0x7bcd;
    long lResult = 0;

    printf("[sHighBits1 + sLowBits1] ";

    lResult = CatenateBits16(sHighBits1, sLowBits1);
    printf("lResult = %08x ", lResult, lResult);

    lResult = CatenateBits16(sHighBits2, sLowBits1);
    printf("lResult = %08x ", lResult, lResult);

    lResult = CatenateBits16(usHighBits3, sLowBits1);
    printf("lResult = %08x ", lResult, lResult);
    }
    /////////////////////////////////////////////////

    運(yùn)行結(jié)果為:

    [sHighBits1 + sLowBits1]
    lResult = 7fff7bcd
    lResult = 8f127bcd
    lResult = ff127bcd

    嗯,運(yùn)行很正確嘛……于是我們就放心的在自己的程序中使用起這個(gè)函數(shù)來(lái)了。

    可是忽然有一天,我們的一個(gè)程序無(wú)論如何結(jié)果都不對(duì)!經(jīng)過(guò)n個(gè)小時(shí)的檢查和調(diào)試,最后終于追蹤到……CatenateBits16() ???它的返回值居然是錯(cuò)的??!

    “郁悶!”你說(shuō),“這個(gè)函數(shù)怎么會(huì)有問(wèn)題呢!?”

    可是,更郁悶的還在后頭呢,因?yàn)槟惆殉绦蛑械妮斎肓孔鳛閰?shù),在一個(gè)簡(jiǎn)單的main()里面單步調(diào)試:

    /////////////////////////////////////////////////
    int main()
    {
    short sHighBits1 = 0x7FFF;
    short sHighBits2 = 0x8F12;
    unsigned short usHighBits3 = 0x8F12;

    short sLowBits1 = 0x7BCD; //你實(shí)際使用的參數(shù)
    short sLowBits2 = 0x8BCD; //你實(shí)際使用的參數(shù)

    long lResult = 0;

    printf("[sHighBits1 + sLowBits1] ";

    lResult = CatenateBits16(sHighBits1, sLowBits1);
    printf("lResult = %08x ", lResult, lResult);

    lResult = CatenateBits16(sHighBits2, sLowBits1);
    printf("lResult = %08x ", lResult, lResult);

    lResult = CatenateBits16(usHighBits3, sLowBits1);
    printf("lResult = %08x ", lResult, lResult);

    printf(" [sHighBits1 + sLowBits2] ";

    lResult = CatenateBits16(sHighBits1, sLowBits2);
    printf("lResult = %08x ", lResult, lResult);

    lResult = CatenateBits16(sHighBits2, sLowBits2);
    printf("lResult = %08x ", lResult, lResult);

    lResult = CatenateBits16(usHighBits3, sLowBits2);
    printf("lResult = %08x ", lResult, lResult);

    return 0;
    }
    /////////////////////////////////////////////////

    發(fā)現(xiàn)結(jié)果竟然是:

    [sHighBits1 + sLowBits1]
    lResult = 7fff7bcd
    lResult = 8f127bcd
    lResult = 8f127bcd

    [sHighBits1 + sLowBits2]
    lResult = ffff8bcd //oops!
    lResult = ffff8bcd //oops!
    lResult = ffff8bcd //oops!

    前一次還好好的,后一次就ffff了?X檔案?


    [X檔案的真相]:

    注意那兩個(gè)我們用來(lái)當(dāng)作低16位值的sLowBits1和sLowBits2。

    已知:
    使用 sLowBits1 = 0x7bcd 時(shí),函數(shù)返回正確的值;
    使用 sLowBits2 = 0x8bcd 時(shí),函數(shù)中發(fā)生X檔案。

    那么,sLowBits1與sLowBits2有什么區(qū)別?

    注意了,sLowBits1和sLowBits2都是short型(而不是unsigned short),所以在這里,sLowBits1代表一個(gè)正數(shù)值,而sLowBits2卻代表了一個(gè)負(fù)數(shù)值(因?yàn)?即是二進(jìn)制1000,sLowBits2最高位是1)。

    再看CatenateBits16()函數(shù):

    /////////////////////////////////////////////////
    long CatenateBits16(short sHighBits, short sLowBits)
    {
    long lResult = 0; /* 32位值的臨時(shí)變量*/

    /* 將第一個(gè)16位值放入32位值的高16位 */
    lResult = sHighBits;
    lResult <<= 16;

    /* 清除32位值的低16位 */
    lResult &= 0xFFFF0000;

    /* 將第二個(gè)16位值放入32位值的低16位 */
    lResult |= (long)sLowBits; //注意這一句!?。。?/p>

    return lResult;
    }
    /////////////////////////////////////////////////

    如果我們?cè)诤瘮?shù)中用

    printf("sLowBits = %04x ", sLowBits);

    打印傳入的sLowBits值,會(huì)發(fā)現(xiàn)

    sLowBits = 0x7bcd 時(shí),打印結(jié)果為

    sLowBits = 7bcd

    而sLowBits = 0x8bcd時(shí),打印結(jié)果為

    sLowBits = ffff8bcd

    是的,即使用%04x也打印出8位十六進(jìn)制。

    因此,我們看出來(lái)了:

    當(dāng)sLowBits = 0x8bcd時(shí),函數(shù)中 "lResult |= (long)sLowBits;" 這一句執(zhí)行,會(huì)先將sLowBits轉(zhuǎn)換為

    0xffff8bcd

    再與lResult做或運(yùn)算。由于現(xiàn)在lResult的值為 0xXXXX0000 (其中XXXX是任何值),所以顯然,無(wú)論sHighBits是什么值,最后結(jié)果都會(huì)是

    0xffff8bcd

    而當(dāng)sLowBits = 0x7bcd時(shí),函數(shù)中 "lResult |= (long)sLowBits;" 這一句執(zhí)行,會(huì)先將sLowBits轉(zhuǎn)換為

    0x00007bcd

    再與lResult做或運(yùn)算。這樣做或運(yùn)算出來(lái)的結(jié)果當(dāng)然就是對(duì)的。

    也就是說(shuō),CatenateBits16()在sLowBits的最高位為0的時(shí)候表現(xiàn)正常,而在最高位為1的時(shí)候出現(xiàn)偏差。

    [教訓(xùn):在某些情況下作位運(yùn)算和位處理的時(shí)候,考慮使用無(wú)符號(hào)數(shù)值——因?yàn)檫@個(gè)時(shí)候往往不需要處理符號(hào)。即使你需要的有符號(hào)的數(shù)值,那么也應(yīng)該考慮自行在調(diào)用CatenateBits16()前后做轉(zhuǎn)換——畢竟在位處理中,有符號(hào)數(shù)值相當(dāng)詭異!]

    下面這個(gè)CatenateBits16()版本應(yīng)該會(huì)好一些:

    /////////////////////////////////////////////////
    unsigned long CatenateBits16(unsigned short sHighBits, unsigned short sLowBits)
    {
    long lResult = 0;

    /* 將第一個(gè)16位值放入32位值的高16位 */
    lResult = sHighBits;
    lResult <<= 16;

    /* 清除32位值的低16位 */
    lResult &= 0xFFFF0000;

    /* 將第二個(gè)16位值放入32位值的低16位 */
    lResult |= (long)sLowBits & 0x0000FFFF;

    return lResult;
    }
    /////////////////////////////////////////////////

    注意其中的 "lResult |= (long)sLowBits & 0x0000FFFF;"。事實(shí)上,現(xiàn)在即使我們把CatenateBits16()函數(shù)的參數(shù)(特別是sLowBits)聲明為short,結(jié)果也會(huì)是對(duì)的。

    如果有一天你把一只兔子扔給一只老虎,老虎把兔子吃了,第二天把一只老鼠扔給它,它又吃了,那么說(shuō)明第一天你看錯(cuò)了:它本來(lái)就是一只貓。


    全文結(jié)束

     

     

     

     

    C++類(lèi)中可以有靜態(tài)構(gòu)造函數(shù)嗎? [問(wèn)題點(diǎn)數(shù):50分]   收藏

    hly_ysu211
    hly_ysu211
    等級(jí):
    結(jié)帖率:56%
    樓主 發(fā)表于: 2009-06-23 13:35:51
    C++類(lèi)中可以有靜態(tài)構(gòu)造函數(shù)嗎?
    分享到: 
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理 回復(fù)次數(shù):40

    lw1a2
    lw1a2
    等級(jí):
    #1 得分:0 回復(fù)于: 2009-06-23 13:37:35
    不可以
    SiteApp大賽火熱進(jìn)行中!對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    chenzhp
    chenzhp
    等級(jí):
    #2 得分:0 回復(fù)于: 2009-06-23 13:40:16
    不行
    2013年7月微軟MVP當(dāng)選名單揭曉!對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    neohope
    neohope
    等級(jí):
    3
    #3 得分:0 回復(fù)于: 2009-06-23 13:45:04
    呵呵,不可以的
    參與Linux應(yīng)用有獎(jiǎng)?wù){(diào)查,贏取MAC筆記本、HTC One手機(jī)!對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    lingyin55
    lingyin55
    等級(jí):
    #4 得分:0 回復(fù)于: 2009-06-23 13:45:33
    c#就有,不要混淆了。
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    Loaden 
    老鄧
    等級(jí):
    22
    #5 得分:0 回復(fù)于: 2009-06-23 13:46:23
    不可以的
    靜態(tài)類(lèi)不能生成實(shí)例的
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    Jalien
    Jalien
    等級(jí):
    #6 得分:0 回復(fù)于: 2009-06-23 13:52:02
    沒(méi)有吧,不知道lz想干什么,如果要想不用實(shí)例化對(duì)象就可以用它的方法的話(huà)把它的方法都聲明為static就行,然后類(lèi)名::方法名 就可以訪(fǎng)問(wèn)它的方法。(方法中的變量都必須為static變量)
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    vangoals
    vangoals
    等級(jí):
    #7 得分:0 回復(fù)于: 2009-06-23 13:53:36
    樓主想要C++ 版的 Singleton ?
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    hly_ysu211
    hly_ysu211
    等級(jí):
    #8 得分:0 回復(fù)于: 2009-06-23 13:56:30
    那如果一個(gè)類(lèi)中有一個(gè)類(lèi)類(lèi)型的成員變量,而這個(gè)類(lèi)型又沒(méi)有默認(rèn)構(gòu)造函數(shù),這個(gè)時(shí)候怎么辦呢???望解釋?zhuān)。?
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    jhony_lee
    jhony_lee
    等級(jí):
    #9 得分:0 回復(fù)于: 2009-06-23 13:58:49
    不可以滴
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    goodname
    goodname
    等級(jí):
    32
    #10 得分:0 回復(fù)于: 2009-06-23 13:59:52
    可以在初始化列表里面調(diào)用一下這個(gè)類(lèi)類(lèi)型的構(gòu)造函數(shù),當(dāng)然你需要給它合時(shí)的參數(shù)
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    lire1213
    lire1213
    等級(jí):
    #11 得分:0 回復(fù)于: 2009-06-23 14:01:29
    不可以
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    zhangxun2007
    zhangxun2007
    等級(jí):
    #12 得分:0 回復(fù)于: 2009-06-23 14:02:20
    引用 5 樓 Loaden 的回復(fù):
    不可以的  靜態(tài)類(lèi)不能生成實(shí)例的


    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    amossavez
    amossavez
    等級(jí):
    #13 得分:0 回復(fù)于: 2009-06-23 14:02:26
    是不可以的!!!
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    pathuang68
    pathuang68
    等級(jí):
    2
    #14 得分:0 回復(fù)于: 2009-06-23 14:05:19
    這個(gè)話(huà)題很有意思。
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    hikaliv
    hikaliv
    等級(jí):
    #15 得分:0 回復(fù)于: 2009-06-23 14:47:34
    當(dāng)然可以有。
    你理解靜態(tài)構(gòu)造函數(shù)么?
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    Jalien
    Jalien
    等級(jí):
    #16 得分:0 回復(fù)于: 2009-06-23 15:45:53
    試一下不就知道了:
    C/C++ code
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Test{
    public:
        static Test(){
        }
    };
     
    int main(){
        Test t;
    }

    vs2008報(bào)錯(cuò):error C2574: “Test::Test(void)”: 不能聲明為靜態(tài)的
    gcc報(bào)錯(cuò):   error: constructor cannot be static member function


    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    Jalien
    Jalien
    等級(jí):
    #17 得分:0 回復(fù)于: 2009-06-23 15:48:29
    Borland5.6.4也編譯不過(guò):Error E2092 cons.cpp 3: Storage class 'static' is not allowed here
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    hikaliv
    hikaliv
    等級(jí):
    #18 得分:0 回復(fù)于: 2009-06-23 15:56:29
    好像真的不行……

    C++做不到的呵……
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    hikaliv
    hikaliv
    等級(jí):
    #19 得分:0 回復(fù)于: 2009-06-23 15:56:55
    又說(shuō)錯(cuò)話(huà)了……
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    Kevin_Perkins
    Kevin_Perkins
    等級(jí):
    #20 得分:0 回復(fù)于: 2009-06-23 16:05:46
    不可以有靜態(tài)的構(gòu)造函數(shù)。
    靜態(tài)成員函數(shù)和靜態(tài)成員變量是屬于類(lèi)的,不是屬于某個(gè)類(lèi)的實(shí)例的。從這個(gè)意義上講,精通構(gòu)造函數(shù)毫無(wú)意義。
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    adventurelw
    adventurelw
    等級(jí):
    #21 得分:0 回復(fù)于: 2009-06-23 16:21:00
    引用 8 樓 hly_ysu211 的回復(fù):
    那如果一個(gè)類(lèi)中有一個(gè)類(lèi)類(lèi)型的成員變量,而這個(gè)類(lèi)型又沒(méi)有默認(rèn)構(gòu)造函數(shù),這個(gè)時(shí)候怎么辦呢???望解釋?zhuān)。?/p>

    除非你人為將默認(rèn)構(gòu)造函數(shù)聲明為私有或保護(hù)的[1],以及聲明了其他非默認(rèn)構(gòu)造函數(shù)而沒(méi)有聲明默認(rèn)構(gòu)造函數(shù)[2],
    否則不會(huì)沒(méi)有默認(rèn)構(gòu)造函數(shù)。
    第一種情況純粹是跟自己過(guò)不去
    第二種情況可以顯式調(diào)用相關(guān)構(gòu)造函數(shù)
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    Loaden 
    老鄧
    等級(jí):
    22
    #22 得分:0 回復(fù)于: 2009-06-23 16:50:56
    引用 8 樓 hly_ysu211 的回復(fù):
    那如果一個(gè)類(lèi)中有一個(gè)類(lèi)類(lèi)型的成員變量,而這個(gè)類(lèi)型又沒(méi)有默認(rèn)構(gòu)造函數(shù),這個(gè)時(shí)候怎么辦呢???望解釋?zhuān)。?/p>

    怎么會(huì)沒(méi)有默認(rèn)構(gòu)造函數(shù)呢?
    編譯器會(huì)生成一個(gè)。
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    liujinxunhappy08110
    liujinxunhappy08110
    等級(jí):
    #23 得分:0 回復(fù)于: 2009-06-23 16:54:28
    不能有
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    sszwbuaa
    sszwbuaa
    等級(jí):
    #24 得分:0 回復(fù)于: 2009-06-23 17:38:55
    引用 16 樓 Jalien 的回復(fù):
    試一下不就知道了:  C/C++ code class Test{ public:     static Test(){     } }; int main(){     Test t; } vs2008報(bào)錯(cuò):error C2574: “Test::Test(void)”: 不能聲明為靜態(tài)的  gcc報(bào)錯(cuò):  error: constructor cannot be static member function

    實(shí)踐出真知!
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    M0521
    M0521
    等級(jí):
    #25 得分:0 回復(fù)于: 2009-06-23 18:24:00
    C++類(lèi)中可以有靜態(tài)構(gòu)造函數(shù)嗎 ?? 

    如果你私有化 構(gòu)造函數(shù)  那么構(gòu)造函數(shù) 必須從靜態(tài)函數(shù) new 出來(lái) ! 不知道你說(shuō)的是不是這個(gè)意思

    C/C++ code
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
      class A
    {
         explicit A(char * n_world){ strcpy (m_array,n_world);}
         char m_array[32];
    public:
        
         static A * Create(char * world) {
            return new A(world);
         }
        void show(){std::cout<<m_array<<endl;}
    };
     
     
        A a=* A::Create(" C++  !");
        a.show ();
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    nwao7890
    nwao7890
    等級(jí):
    #26 得分:0 回復(fù)于: 2009-06-23 18:30:45
    靜態(tài)成員函數(shù)不能訪(fǎng)問(wèn)一般的數(shù)據(jù)成員,而只能訪(fǎng)問(wèn)靜態(tài)數(shù)據(jù)成員,也只能調(diào)用其他的靜態(tài)成員函數(shù)。

    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    pengzhixi
    pengzhixi
    等級(jí):
    243
    #27 得分:0 回復(fù)于: 2009-06-23 18:38:24
    C++中不可以將構(gòu)造函數(shù)申明為靜態(tài)函數(shù)
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    pengzhixi
    pengzhixi
    等級(jí):
    243
    #28 得分:0 回復(fù)于: 2009-06-23 19:08:54
    構(gòu)造函數(shù)如果為靜態(tài)的話(huà),this指針怎么處理呢?
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    ztz0223
    好孩子的阿佐
    等級(jí):
    #29 得分:0 回復(fù)于: 2009-06-23 20:12:00

    構(gòu)造函數(shù)要給每一個(gè)對(duì)象一個(gè)this指針
    如果可以是靜態(tài)的,它如何構(gòu)造和訪(fǎng)問(wèn)this指針?
    明顯是不可以的!
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    leewon1988
    leewon1988
    等級(jí):
    #30 得分:0 回復(fù)于: 2009-06-23 20:20:23
    顯然不可能,靜態(tài)的是所有類(lèi)共有的,構(gòu)造函數(shù)是什么?是構(gòu)造一個(gè)對(duì)象的,靜態(tài)的怎么會(huì)屬于一個(gè)對(duì)象呢?
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    lconline
    lconline
    等級(jí):
    #31 得分:0 回復(fù)于: 2010-06-02 09:46:56
    引用 22 樓 loaden 的回復(fù):
    引用 8 樓 hly_ysu211 的回復(fù): 那如果一個(gè)類(lèi)中有一個(gè)類(lèi)類(lèi)型的成員變量,而這個(gè)類(lèi)型又沒(méi)有默認(rèn)構(gòu)造函數(shù),這個(gè)時(shí)候怎么辦呢???望解釋?zhuān)?!怎么?huì)沒(méi)有默認(rèn)構(gòu)造函數(shù)呢?
    編譯器會(huì)生成一個(gè)。


    如果沒(méi)有定義默認(rèn)構(gòu)造函數(shù),也沒(méi)有定義其它的構(gòu)造函數(shù)。對(duì)象不會(huì)被初始化,也不會(huì)調(diào)用任何構(gòu)造函數(shù)。

    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    patricxuqi
    patricxuqi
    等級(jí):
    #32 得分:0 回復(fù)于: 2010-06-02 11:23:32
    引用 28 樓 pengzhixi 的回復(fù):
    構(gòu)造函數(shù)如果為靜態(tài)的話(huà),this指針怎么處理呢?

    靜態(tài)函數(shù)是類(lèi)屬于類(lèi)的,不是屬于對(duì)象的。而C++正是通過(guò)this指針來(lái)區(qū)分對(duì)象。靜態(tài)函數(shù)與非靜態(tài)函數(shù)的區(qū)別就是不接受這個(gè)this。如果構(gòu)造函數(shù)不接受this的話(huà),又怎么能建立某個(gè)具體對(duì)象呢?
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    blpluto
    blpluto
    等級(jí):
    #33 得分:0 回復(fù)于: 2010-06-02 11:30:19
    不可以。這是因?yàn)镃++的特性原因……

    但是這個(gè)過(guò)程卻可以模擬??!
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    waterx
    waterx
    等級(jí):
    #34 得分:0 回復(fù)于: 2010-06-02 11:34:27
    引用 31 樓 lconline 的回復(fù):
    引用 22 樓 loaden 的回復(fù): 引用 8 樓 hly_ysu211 的回復(fù): 那如果一個(gè)類(lèi)中有一個(gè)類(lèi)類(lèi)型的成員變量,而這個(gè)類(lèi)型又沒(méi)有默認(rèn)構(gòu)造函數(shù),這個(gè)時(shí)候怎么辦呢???望解釋?zhuān)。?怎么會(huì)沒(méi)有默認(rèn)構(gòu)造函數(shù)呢? 編譯器會(huì)生成一個(gè)。 如果沒(méi)有定義默認(rèn)構(gòu)造函數(shù),也沒(méi)有定義其它的構(gòu)造函數(shù)。對(duì)象不會(huì)被初始化,也不會(huì)調(diào)用任何構(gòu)造函數(shù)。


    不對(duì),別誤導(dǎo),
    如果類(lèi)沒(méi)有默認(rèn)構(gòu)造函數(shù),而又沒(méi)有調(diào)用別的構(gòu)造函數(shù),編譯會(huì)提示沒(méi)有合適的構(gòu)造函數(shù)調(diào)用
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    ZiyData
    ZiyData
    等級(jí):
    #35 得分:0 回復(fù)于: 2010-06-02 11:47:54
    有什么不好,給了程序員很大的靈活性,可以自己在代碼中控制各個(gè)類(lèi)的初始化順序!
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    ww884203
    ww884203
    等級(jí):
    #36 得分:0 回復(fù)于: 2010-06-02 12:08:58
    不要胡思亂想了
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    magic7004
    magic7004
    等級(jí):
    #37 得分:0 回復(fù)于: 2010-06-02 13:16:02
    靜態(tài)構(gòu)造函數(shù)就算能有,那也是沒(méi)有任何意義的啊。

    構(gòu)造函數(shù)的作用就是對(duì)成員變量和需要的資源進(jìn)行初始化,如果構(gòu)造函數(shù)是靜態(tài)的,那么它就不可以訪(fǎng)問(wèn)成員變量,那么它就無(wú)法實(shí)現(xiàn)構(gòu)造函數(shù)的功能....
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    cyblueboy83
    cyblueboy83
    等級(jí):
    #38 得分:0 回復(fù)于: 2010-06-02 13:56:11
    如果要實(shí)現(xiàn)單體,自己提供一個(gè)靜態(tài)的getinstance函數(shù)吧
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    lthyxy
    那個(gè)無(wú)知的少年
    等級(jí):
    #39 得分:0 回復(fù)于: 2010-06-02 14:07:04
    編譯器會(huì)自動(dòng)生成
    對(duì)我有用[0] 丟個(gè)板磚[0] 引用 | 舉報(bào) | 管理

    softman11
    softman11
    等級(jí):
    #40 得分:0 回復(fù)于: 2010-06-02 14:21:42
    C++不提供這個(gè)支持

    但是C#可以的哈。

     

     

     

     


    C/C++語(yǔ)言void及void指針深層探索2006-08-05 06:00 來(lái)源:blog 作者:蔣濤 責(zé)任編輯:方舟·yesky 評(píng)論(16)
    1.概述

    許多初學(xué)者對(duì)C/C++語(yǔ)言中的void及void指針類(lèi)型不甚理解,因此在使用上出現(xiàn)了一些錯(cuò)誤。本文將對(duì)void關(guān)鍵字的深刻含義進(jìn)行解說(shuō),并詳述void及void指針類(lèi)型的使用方法與技巧。

    2.void的含義

    void的字面意思是“無(wú)類(lèi)型”,void *則為“無(wú)類(lèi)型指針”,void *可以指向任何類(lèi)型的數(shù)據(jù)。

    void幾乎只有“注釋”和限制程序的作用,因?yàn)閺膩?lái)沒(méi)有人會(huì)定義一個(gè)void變量,讓我們?cè)囍鴣?lái)定義:

    void a;

    這行語(yǔ)句編譯時(shí)會(huì)出錯(cuò),提示“illegal use of type 'void'”。不過(guò),即使void a的編譯不會(huì)出錯(cuò),它也沒(méi)有任何實(shí)際意義。

    void真正發(fā)揮的作用在于:

    (1)對(duì)函數(shù)返回的限定;

    (2)對(duì)函數(shù)參數(shù)的限定。

    我們將在第三節(jié)對(duì)以上二點(diǎn)進(jìn)行具體說(shuō)明。

    眾所周知,如果指針p1和p2的類(lèi)型相同,那么我們可以直接在p1和p2間互相賦值;如果p1和p2指向不同的數(shù)據(jù)類(lèi)型,則必須使用強(qiáng)制類(lèi)型轉(zhuǎn)換運(yùn)算符把賦值運(yùn)算符右邊的指針類(lèi)型轉(zhuǎn)換為左邊指針的類(lèi)型。

    例如:

    float *p1;
    int *p2;
    p1 = p2;

    其中p1 = p2語(yǔ)句會(huì)編譯出錯(cuò),提示“'=' : cannot convert from 'int *' to 'float *'”,必須改為:

    p1 = (float *)p2;

    而void *則不同,任何類(lèi)型的指針都可以直接賦值給它,無(wú)需進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換:

    void *p1;
    int *p2;
    p1 = p2;

    但這并不意味著,void *也可以無(wú)需強(qiáng)制類(lèi)型轉(zhuǎn)換地賦給其它類(lèi)型的指針。因?yàn)椤盁o(wú)類(lèi)型”可以包容“有類(lèi)型”,而“有類(lèi)型”則不能包容“無(wú)類(lèi)型”。道理很簡(jiǎn)單,我們可以說(shuō)“男人和女人都是人”,但不能說(shuō)“人是男人”或者“人是女人”。下面的語(yǔ)句編譯出錯(cuò):

    void *p1;
    int *p2;
    p2 = p1;

    提示“'=' : cannot convert from 'void *' to 'int *'”。

    3.void的使用

    下面給出void關(guān)鍵字的使用規(guī)則:

    規(guī)則一如果函數(shù)沒(méi)有返回值,那么應(yīng)聲明為void類(lèi)型

    在C語(yǔ)言中,凡不加返回值類(lèi)型限定的函數(shù),就會(huì)被編譯器作為返回整型值處理。但是許多程序員卻誤以為其為void類(lèi)型。例如:

    add ( int a, int b )
    {
    return a + b;
    }
    int main(int argc, char* argv[])
    {
    printf ( "2 + 3 = %d", add ( 2, 3) );
    }

    程序運(yùn)行的結(jié)果為輸出:

    2 + 3 = 5

    這說(shuō)明不加返回值說(shuō)明的函數(shù)的確為int函數(shù)。

    林銳博士《高質(zhì)量C/C++編程》中提到:“C++語(yǔ)言有很?chē)?yán)格的類(lèi)型安全檢查,不允許上述情況(指函數(shù)不加類(lèi)型聲明)發(fā)生”??墒蔷幾g器并不一定這么認(rèn)定,譬如在Visual C++6.0中上述add函數(shù)的編譯無(wú)錯(cuò)也無(wú)警告且運(yùn)行正確,所以不能寄希望于編譯器會(huì)做嚴(yán)格的類(lèi)型檢查。

    因此,為了避免混亂,我們?cè)诰帉?xiě)C/C++程序時(shí),對(duì)于任何函數(shù)都必須一個(gè)不漏地指定其類(lèi)型。如果函數(shù)沒(méi)有返回值,一定要聲明為void類(lèi)型。這既是程序良好可讀性的需要,也是編程規(guī)范性的要求。另外,加上void類(lèi)型聲明后,也可以發(fā)揮代碼的“自注釋”作用。代碼的“自注釋”即代碼能自己注釋自己。

    規(guī)則二如果函數(shù)無(wú)參數(shù),那么應(yīng)聲明其參數(shù)為void

    在C++語(yǔ)言中聲明一個(gè)這樣的函數(shù):

    int function(void)
    {
    return 1;
    }

    則進(jìn)行下面的調(diào)用是不合法的:

    function(2);

    因?yàn)樵贑++中,函數(shù)參數(shù)為void的意思是這個(gè)函數(shù)不接受任何參數(shù)。

    我們?cè)赥urbo C 2.0中編譯:

    #include "stdio.h"
    fun()
    {
    return 1;
    }
    main()
    {
    printf("%d",fun(2));
    getchar();
    }

    編譯正確且輸出1,這說(shuō)明,在C語(yǔ)言中,可以給無(wú)參數(shù)的函數(shù)傳送任意類(lèi)型的參數(shù),但是在C++編譯器中編譯同樣的代碼則會(huì)出錯(cuò)。在C++中,不能向無(wú)參數(shù)的函數(shù)傳送任何參數(shù),出錯(cuò)提示“'fun' : function does not take 1 parameters”。

    所以,無(wú)論在C還是C++中,若函數(shù)不接受任何參數(shù),一定要指明參數(shù)為void。

    規(guī)則三小心使用void指針類(lèi)型

    按照ANSI(American National Standards Institute)標(biāo)準(zhǔn),不能對(duì)void指針進(jìn)行算法操作,即下列操作都是不合法的:

    void * pvoid;
    pvoid++; //ANSI:錯(cuò)誤
    pvoid += 1; //ANSI:錯(cuò)誤
    //ANSI標(biāo)準(zhǔn)之所以這樣認(rèn)定,是因?yàn)樗鼒?jiān)持:進(jìn)行算法操作的指針必須是確定知道其指向數(shù)據(jù)類(lèi)型大小的。
    //例如:
    int *pint;
    pint++; //ANSI:正確

    pint++的結(jié)果是使其增大sizeof(int)。

    但是大名鼎鼎的GNU(GNU's Not Unix的縮寫(xiě))則不這么認(rèn)定,它指定void *的算法操作與char *一致。

    因此下列語(yǔ)句在GNU編譯器中皆正確:

    pvoid++; //GNU:正確
    pvoid += 1; //GNU:正確

    pvoid++的執(zhí)行結(jié)果是其增大了1。

    在實(shí)際的程序設(shè)計(jì)中,為迎合ANSI標(biāo)準(zhǔn),并提高程序的可移植性,我們可以這樣編寫(xiě)實(shí)現(xiàn)同樣功能的代碼:

    void * pvoid;
    (char *)pvoid++; //ANSI:正確;GNU:正確
    (char *)pvoid += 1; //ANSI:錯(cuò)誤;GNU:正確

    GNU和ANSI還有一些區(qū)別,總體而言,GNU較ANSI更“開(kāi)放”,提供了對(duì)更多語(yǔ)法的支持。但是我們?cè)谡鎸?shí)設(shè)計(jì)時(shí),還是應(yīng)該盡可能地迎合ANSI標(biāo)準(zhǔn)。

    規(guī)則四如果函數(shù)的參數(shù)可以是任意類(lèi)型指針,那么應(yīng)聲明其參數(shù)為void *

    典型的如內(nèi)存操作函數(shù)memcpy和memset的函數(shù)原型分別為:

    void * memcpy(void *dest, const void *src, size_t len);
    void * memset ( void * buffer, int c, size_t num );

    這樣,任何類(lèi)型的指針都可以傳入memcpy和memset中,這也真實(shí)地體現(xiàn)了內(nèi)存操作函數(shù)的意義,因?yàn)樗僮鞯膶?duì)象僅僅是一片內(nèi)存,而不論這片內(nèi)存是什么類(lèi)型。如果memcpy和memset的參數(shù)類(lèi)型不是void *,而是char *,那才叫真的奇怪了!這樣的memcpy和memset明顯不是一個(gè)“純粹的,脫離低級(jí)趣味的”函數(shù)!

    下面的代碼執(zhí)行正確:

    //示例:memset接受任意類(lèi)型指針
    int intarray[100];
    memset ( intarray, 0, 100*sizeof(int) ); //將intarray清0
    //示例:memcpy接受任意類(lèi)型指針
    int intarray1[100], intarray2[100];
    memcpy ( intarray1, intarray2, 100*sizeof(int) ); //將intarray2拷貝給intarray1

    有趣的是,memcpy和memset函數(shù)返回的也是void *類(lèi)型,標(biāo)準(zhǔn)庫(kù)函數(shù)的編寫(xiě)者是多么地富有學(xué)問(wèn)啊!

    規(guī)則五 void不能代表一個(gè)真實(shí)的變量

    下面代碼都企圖讓void代表一個(gè)真實(shí)的變量,因此都是錯(cuò)誤的代碼:

    void a; //錯(cuò)誤
    function(void a); //錯(cuò)誤

    void體現(xiàn)了一種抽象,這個(gè)世界上的變量都是“有類(lèi)型”的,譬如一個(gè)人不是男人就是女人(還有人妖?)。

    void的出現(xiàn)只是為了一種抽象的需要,如果你正確地理解了面向?qū)ο笾小俺橄蠡?lèi)”的概念,也很容易理解void數(shù)據(jù)類(lèi)型。正如不能給抽象基類(lèi)定義一個(gè)實(shí)例,我們也不能定義一個(gè)void(讓我們類(lèi)比的稱(chēng)void為“抽象數(shù)據(jù)類(lèi)型”)變量。

    4.總結(jié)

    小小的void蘊(yùn)藏著很豐富的設(shè)計(jì)哲學(xué),作為一名程序設(shè)計(jì)人員,對(duì)問(wèn)題進(jìn)行深一個(gè)層次的思考必然使我們受益匪淺

     

     

     

     

     

     

    解決C++ 無(wú)法從void 轉(zhuǎn)換為L(zhǎng)RESULT的方法詳解
    發(fā)布:jingxian 字體:[增加 減小] 類(lèi)型:轉(zhuǎn)載
    本篇文章是對(duì)C++中無(wú)法從void轉(zhuǎn)換為L(zhǎng)RESULT的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    這個(gè)應(yīng)該是一個(gè)MFC程序,ON_MESSAGE是添加消息響應(yīng)函數(shù),這句話(huà)就是添加熱鍵WM_HOTKEY的響應(yīng)函數(shù)。當(dāng)你注冊(cè)了熱鍵之后,當(dāng)用戶(hù)按下熱鍵,會(huì)執(zhí)行OnHotKey函數(shù)來(lái)處理這個(gè)消息。錯(cuò)誤就應(yīng)該是OnHotKey這個(gè)函數(shù)的聲明錯(cuò)誤了,返回值應(yīng)該是LRESULT. VS2008對(duì)消息的檢查更為嚴(yán)格,以前在VC6下完全正常運(yùn)行的消息映射在VS2008下編譯不通過(guò)

    ON_MESSAGE(WM_message,OnMyMessage);
    OnMessage返回值必須為L(zhǎng)RESULT,其形式為:afx_msg LRESULT OnMessage(WPARAM, LPARAM);
    如果不符合,則有錯(cuò)誤提示:error C2440: “static_cast”:無(wú)法從“void (__thiscall CMainFrame::* )(void)”轉(zhuǎn)換為“LRESULT (__thiscall CWnd::* )(WPARAM,LPARAM)”

    解決方法如下:首先,把原來(lái)的消息函數(shù)返回值類(lèi)型改為L(zhǎng)RESULT,函數(shù)內(nèi)可以隨便寫(xiě)個(gè)return 0;然后消息函數(shù)的參數(shù)必須改寫(xiě)成(WPARAM wParam,LPARAM lParam)而不論這兩個(gè)。

     

     


    深入const int *p與int * const p的區(qū)別詳解(常量指針與指向常量的指針)
    發(fā)布:jingxian 字體:[增加 減小] 類(lèi)型:轉(zhuǎn)載
    本篇文章是對(duì)const int *p與int * const p的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    對(duì)于指針和常量,有以下三種形式都是正確的:
    復(fù)制代碼 代碼如下:

    const char * myPtr = &char_A;//指向常量的指針
    char * const myPtr = &char_A;//常量的指針
    const char * const myPtr = &char_A;//指向常量的常量指針

    下面依次對(duì)這三種類(lèi)型進(jìn)行介紹。
    因?yàn)?操作符是左操作符,左操作符的優(yōu)先級(jí)是從右到左,對(duì)于
    1.常量指針(Constant Pointers)
    復(fù)制代碼 代碼如下:

    int * const p

    先看const再看* ,是p是一個(gè)常量類(lèi)型的指針,不能修改這個(gè)指針的指向,但是這個(gè)指針?biāo)赶虻牡刂飞洗鎯?chǔ)的值可以修改。
    實(shí)例1:
    復(fù)制代碼 代碼如下:

    #include<iostream>
    #include<stdlib.h>
    using namespace std;
    void main()
    {
        int i1=30;
        int i2=40;
        int * const pi=&i1;//這里的pi指針式常量。
        //pi=&i2;     //注意這里,pi不能再這樣重新賦值了,即不能再指向另一個(gè)新地址。所以我已經(jīng)注釋了它。
        printf("%d\n", *pi ) ;   //輸出是30
        i1=80;     //5.想想看:這里能用*pi=80;來(lái)代替嗎?可以,這里可以通過(guò)*pi修改i1的值。
        printf("%d\n", *pi ) ;   //輸出是80
        system("pause");
    }

    實(shí)例2:
    復(fù)制代碼 代碼如下:

    char char_A = 'A';
    char char_B = 'B';

    char * const myPtr = &char_A;
    myPtr = &char_B;    // error - can't change address of myPtr

    2.指向常量的指針(Pointers to Constants)
    復(fù)制代碼 代碼如下:

    const int *p

    先看*再看const,定義一個(gè)指針指向一個(gè)常量,不能通過(guò)指針來(lái)修改這個(gè)指針指向的值。
    實(shí)例3:
    復(fù)制代碼 代碼如下:

    #include<iostream>
    #include<stdlib.h>
    using namespace std;
    void main()
    {
        int i1=30;
        int i2=40;
        const int * pi=&i1;
        printf("%d\n", *pi ) ;   //輸出是30
        pi=&i2;     //注意這里,pi可以在任意時(shí)候重新賦值一個(gè)新內(nèi)存地址
        i2=80;     //想想看:這里能用*pi=80;來(lái)代替嗎?當(dāng)然不能
        printf("%d\n", *pi ) ;   //輸出是80
        system("pause");
    }

    實(shí)例4
    復(fù)制代碼 代碼如下:

    char char_A = 'A';
    const char * myPtr = &char_A;
    *myPtr = 'J';    // error - can't change value of *myPtr

    所以指針p所指向的整型數(shù)是一個(gè)常量,其值不能被修改。
    3.指向常量的常量指針
    對(duì)于“指向常量的常量指針”,就必須同時(shí)滿(mǎn)足上述1和2中的內(nèi)容,既不可以修改指針的值,也不可以修改指針指向的值。
    4.引入字符數(shù)組和字符指針
    字符數(shù)組和字符指針的定義如下:
    復(fù)制代碼 代碼如下:

    char a[] = "I Love You!"; //定義了一個(gè)字符數(shù)組
    char *p = "I Love You!";  //定義了一個(gè)字符指針

    可以將a理解為常量指針,而p為指向常量的指針,代碼實(shí)例如下:
    復(fù)制代碼 代碼如下:

    #include<iostream>
    #include<stdlib.h>
    using namespace std;
    void main()
    {
        char a[] = "I Love You!"; //定義了一個(gè)字符數(shù)組,數(shù)組名a是一個(gè)常量指針,指向的位置不變,都是數(shù)組第一個(gè)元素的位置
        char *p = "I Love You!";  //定義了一個(gè)字符指針,指針p指向一個(gè)字符串常量,這個(gè)字符串常量不能修改
        //*(p+1)='a';//錯(cuò)誤,不可以修改指針指向的值,所以這里注釋掉。
        a[1]='a';//常量指針,不能修改指針的值,但是可以修改指針?biāo)赶虻闹怠?
        //a=p;//錯(cuò)誤,a是常量指針,不可修改其值。
        cout<<a<<endl;
        cout<<p<<endl;
        cout<<a[1]<<endl;
        cout<<*(p+2)<<endl;
        system("pause");
    }

    輸出值為:
    IaLove You!
    I Love You!
    a
    L

     

     

     

     

     

     

     

     

     

    C++
    我們將從指針的語(yǔ)法和使用并結(jié)合例子來(lái)討論他們的區(qū)別。
     
    Void 指針:
     
    Cpp代碼 
    void * pointer_variable; 
    void這是是作為一個(gè)關(guān)鍵字來(lái)使用。
    參考指針的定義和使用,我們知道所定義指針的數(shù)據(jù)類(lèi)型同指針?biāo)傅臄?shù)據(jù)類(lèi)型是一致的。所分配給指針的地址也必須跟指針類(lèi)型一樣。
    例如:
    Cpp代碼 
    int i; 
    float f; 
    int* exf; 
    float* test; 
    then 
    exf=&i;  
     
    int類(lèi)型指針指向int變量的地址空間,所以是對(duì)的。
    如果寫(xiě)成:
    Cpp代碼 
    exf=&f;  
    這條語(yǔ)句就會(huì)產(chǎn)生錯(cuò)誤。因?yàn)閕nt類(lèi)型的指針指向的是一塊float變量的地址空間。同樣,如果我們?cè)噲D把float類(lèi)型的指針指向一塊int類(lèi)型的地址空間,也是錯(cuò)誤的,例如:
    Cpp代碼 
    test=&i;  
    上面一條語(yǔ)句將會(huì)報(bào)錯(cuò)。
     
    void類(lèi)型指針是可以用來(lái)指向任何數(shù)據(jù)類(lèi)型的特殊指針。
    使用前面的例子,如果我們手動(dòng)聲明一個(gè)void類(lèi)型指針:
    Cpp代碼 
    void* sample;  
    在前面的例子中,如果我們定義的一個(gè)void類(lèi)型指針去指向一個(gè)float變量的地址空間是完全正確的。
    Cpp代碼 
    sample=&f;  
    同樣,如果我們把這個(gè)void類(lèi)型指針去指向一個(gè)int類(lèi)型的地址空間也是正確的:
    Cpp代碼 
    sample=&i;  
    void(類(lèi)型)指針,是一種特殊的指針,它足夠靈巧的指向任何數(shù)據(jù)類(lèi)型的地址空間。當(dāng)然它也具有一定的局限:
    在我們要取得指針?biāo)傅刂房臻g的數(shù)據(jù)的時(shí)候使用的是 ‘*’操作符,程序員必須清楚了解到對(duì)于void指針不能使用這種方式來(lái)取得指針?biāo)傅膬?nèi)容。因?yàn)橹苯尤?nèi)容是不允許的。而必須把void指針轉(zhuǎn)換成其他任何valid數(shù)據(jù)類(lèi)型的指針,比如char,int,float等類(lèi)型的指針,之后才能使用'*'取出指針的內(nèi)容。這就是所謂的類(lèi)型轉(zhuǎn)換的概念。
     
    NULL pointer(空指針):
     
    NULL指針的概念不同于前面所說(shuō)的void指針。NULL指針是任何數(shù)據(jù)類(lèi)型指針的一種,并且使用0作為初始值(譯者:這個(gè)好像要跟操作系統(tǒng)有關(guān),有的系統(tǒng)把NULL 指針指向0地址,其他可能指向非零地址,對(duì)吧?有異議請(qǐng)留言)。當(dāng)然這個(gè)不是強(qiáng)制性的。其表明NULL指針并未指向任何一塊合法的(valid)的地址空間。
    舉例:
    Cpp代碼 
    int* exforsys; 
    exforsys=0;  
    以上的聲明表示exforsys是一個(gè)int類(lèi)型的指針,但其不指向任何有效的地址空間,這表明exforsys有一個(gè)空指針值(0)。
     
    Void指針和NULL指針的區(qū)別:
    Void指針是一個(gè)可以指向任何數(shù)據(jù)類(lèi)型的特殊指針。NULL指針可是是任何數(shù)據(jù)類(lèi)型的但其不指向任何有效的地址空間或者引用。區(qū)分空指針和指針未被初始化是很關(guān)鍵的,比如,假如程序員寫(xiě)下:
    Cpp代碼 
    #include <iostream.h> 
    int *exforsys; 
    void main() 

      *exforsys=100; 
    }  
    上面程序代碼的輸出如下:
     
    NULL POINTER ASSIGNMENT
    上面的程序拋出運(yùn)行時(shí)的錯(cuò)誤。表明指針變量exforsys還沒(méi)有被分配任何有效的地址空間,并且試圖存取0地址空間就產(chǎn)生了錯(cuò)誤信息。
     
    Author: UNKNOWN
    Original Paper URL: http://www.exforsys.com/tutorials/c-plus-plus/c-plus-plus-void-pointer-and-null-pointer.html
     
    更多關(guān)于NULL指針的AQ&A,參考:http://c-faq.com/null/
    聲明:ITeye文章版權(quán)屬于作者,受法律保護(hù)。沒(méi)有作者書(shū)面許可不得轉(zhuǎn)載。
    推薦鏈接
    Java開(kāi)發(fā)新方式:專(zhuān)注UI,快速開(kāi)發(fā)!
    返回頂樓        
    agurick
    等級(jí): 初級(jí)會(huì)員

    性別:
    文章: 59
    積分: 10
    來(lái)自: 陜西

       發(fā)表時(shí)間:2009-04-25 
    NULL指針代表一個(gè)指針的值。
    void 指針代表一個(gè)指針的類(lèi)型,差的遠(yuǎn)了去。
    返回頂樓         回帖地址0 0 請(qǐng)登錄后投票
    zhuqimeng
    等級(jí): 初級(jí)會(huì)員

    性別:
    文章: 28
    積分: 30
    來(lái)自: 徐州

       發(fā)表時(shí)間:2009-05-20 
    agurick 寫(xiě)道
    NULL指針代表一個(gè)指針的值。
    void 指針代表一個(gè)指針的類(lèi)型,差的遠(yuǎn)了去。

    一針見(jiàn)血

     

     

     

     

    C++中,為什么必須用造型來(lái)轉(zhuǎn)換*void

    上一節(jié) 下一節(jié) 返回目錄編輯/糾錯(cuò)/意見(jiàn)關(guān)注(146)更新:2012-12-31
    分享到0
    在C 語(yǔ)言中,你可以隱式地將*void 轉(zhuǎn)換為*T。這是不安全的??紤]一下:
    #include<stdio.h>
    int main(){
        char i = 0;
        char j = 0;
        char* p = &i;
        void* q = p;
        int* pp = q; /* 不安全的,在C 中可以,C++不行 */
        printf("%d %d\n",i,j);
        *pp = -1; /* 覆蓋了從i 開(kāi)始的內(nèi)存 */
        printf("%d %d\n",i,j);
    }

    使用一個(gè)并不指向T 類(lèi)型的T*將是一場(chǎng)災(zāi)難。因此,在C++中,如果從一個(gè)void*得到一個(gè)T*,你必須進(jìn)行顯式轉(zhuǎn)換。舉例來(lái)說(shuō),要得到上列程序的這個(gè)令人別扭的效果,你可以這樣寫(xiě):
        int* pp = (int*)q;

    或者使用一個(gè)新的類(lèi)型造型,以使這種沒(méi)有檢查的類(lèi)型轉(zhuǎn)換操作變得更加清晰:
        int* pp = static_cast<int*>(q);

    造型被最好地避免了。

    在C 語(yǔ)言中,這種不安全的轉(zhuǎn)換最常見(jiàn)的應(yīng)用之一,是將malloc()的結(jié)果賦予一個(gè)合適的指針。例如:
        int* p = malloc(sizeof(int));

    在C++中,使用類(lèi)型安全的new 操作符:
        int* p = new int;

    附帶地,new 操作符還提供了勝過(guò)malloc()的新特性:
        new 不會(huì)偶然分配錯(cuò)誤的內(nèi)存數(shù)量;
        new 會(huì)隱式地檢查內(nèi)存耗盡情況,而且
        new 提供了初始化。

    舉例:
        typedef std::complex<double> cmplx;
        /* C 風(fēng)格: */
        cmplx* p = (cmplx*)malloc(sizeof(int)); /* 錯(cuò)誤:類(lèi)型不正確 */
        /* 忘記測(cè)試p==0 */
        if (*p == 7) { /* ... */ } /* 糟糕,忘記了初始化*p */
        // C++風(fēng)格:
        cmplx* q = new cmplx(1,2); // 如果內(nèi)存耗盡,將拋出一個(gè)bad_alloc 異常
        if (*q == 7) { /* ... */ }

     

     

     


    C++拷貝構(gòu)造函數(shù)(深拷貝,淺拷貝)

    對(duì)于普通類(lèi)型的對(duì)象來(lái)說(shuō),它們之間的復(fù)制是很簡(jiǎn)單的,例如:
    int a=88;
    int b=a;
    而類(lèi)對(duì)象與普通對(duì)象不同,類(lèi)對(duì)象內(nèi)部結(jié)構(gòu)一般較為復(fù)雜,存在各種成員變量。下面看一個(gè)類(lèi)對(duì)象拷貝的簡(jiǎn)單例子。

    #include <iostream>
    using namespace std;

    class CExample {
    private:
        int a;
    public:
        CExample(int b)
        { a=b;}
        void Show ()
        {
            cout<<a<<endl;
        }
    };

    int main()
    {
        CExample A(100);
        CExample B=A;
        B.Show ();
        return 0;
    }
    運(yùn)行程序,屏幕輸出100。從以上代碼的運(yùn)行結(jié)果可以看出,系統(tǒng)為對(duì)象B分配了內(nèi)存并完成了與對(duì)象A的復(fù)制過(guò)程。就類(lèi)對(duì)象而言,相同類(lèi)型的類(lèi)對(duì)象是通過(guò)拷貝構(gòu)造函數(shù)來(lái)完成整個(gè)復(fù)制過(guò)程的。下面舉例說(shuō)明拷貝構(gòu)造函數(shù)的工作過(guò)程。

    #include <iostream>
    using namespace std;

    class CExample {
    private:
        int a;
    public:
        CExample(int b)
        { a=b;}
       
        CExample(const CExample& C)
        {
            a=C.a;
        }
        void Show ()
        {
            cout<<a<<endl;
        }
    };

    int main()
    {
        CExample A(100);
        CExample B=A;
        B.Show ();
        return 0;
    }
    CExample(const CExample& C)就是我們自定義的拷貝構(gòu)造函數(shù)??梢?jiàn),拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),函數(shù)的名稱(chēng)必須和類(lèi)名稱(chēng)一致,它的唯一的一個(gè)參數(shù)是本類(lèi)型的一個(gè)引用變量,該參數(shù)是const類(lèi)型,不可變的。例如:類(lèi)X的拷貝構(gòu)造函數(shù)的形式為X(X& x)。

    當(dāng)用一個(gè)已初始化過(guò)了的自定義類(lèi)類(lèi)型對(duì)象去初始化另一個(gè)新構(gòu)造的對(duì)象的時(shí)候,拷貝構(gòu)造函數(shù)就會(huì)被自動(dòng)調(diào)用。也就是說(shuō),當(dāng)類(lèi)的對(duì)象需要拷貝時(shí),拷貝構(gòu)造函數(shù)將會(huì)被調(diào)用。以下情況都會(huì)調(diào)用拷貝構(gòu)造函數(shù):
    一個(gè)對(duì)象以值傳遞的方式傳入函數(shù)體
    一個(gè)對(duì)象以值傳遞的方式從函數(shù)返回
    一個(gè)對(duì)象需要通過(guò)另外一個(gè)對(duì)象進(jìn)行初始化。

    如果在類(lèi)中沒(méi)有顯式地聲明一個(gè)拷貝構(gòu)造函數(shù),那么,編譯器將會(huì)自動(dòng)生成一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù),該構(gòu)造函數(shù)完成對(duì)象之間的位拷貝。位拷貝又稱(chēng)淺拷貝,后面將進(jìn)行說(shuō)明。

    自定義拷貝構(gòu)造函數(shù)是一種良好的編程風(fēng)格,它可以阻止編譯器形成默認(rèn)的拷貝構(gòu)造函數(shù),提高源碼效率。

    淺拷貝和深拷貝

    在某些狀況下,類(lèi)內(nèi)成員變量需要?jiǎng)討B(tài)開(kāi)辟堆內(nèi)存,如果實(shí)行位拷貝,也就是把對(duì)象里的值完全復(fù)制給另一個(gè)對(duì)象,如A=B。這時(shí),如果B中有一個(gè)成員變量指針已經(jīng)申請(qǐng)了內(nèi)存,那A中的那個(gè)成員變量也指向同一塊內(nèi)存。這就出現(xiàn)了問(wèn)題:當(dāng)B把內(nèi)存釋放了(如:析構(gòu)),這時(shí)A內(nèi)的指針就是野指針了,出現(xiàn)運(yùn)行錯(cuò)誤。

    深拷貝和淺拷貝可以簡(jiǎn)單理解為:如果一個(gè)類(lèi)擁有資源,當(dāng)這個(gè)類(lèi)的對(duì)象發(fā)生復(fù)制過(guò)程的時(shí)候,資源重新分配,這個(gè)過(guò)程就是深拷貝,反之,沒(méi)有重新分配資源,就是淺拷貝。下面舉個(gè)深拷貝的例子。

    #include <iostream>
    using namespace std;
    class CA
    {
    public:
    CA(int b,char* cstr)
    {
    a=b;
    str=new char[b];
    strcpy(str,cstr);
    }
    CA(const CA& C)
    {
    a=C.a;
    str=new char[a]; //深拷貝
    if(str!=0)
    strcpy(str,C.str);
    }
    void Show()
    {
    cout<<str<<endl;
    }
    ~CA()
    {
    delete str;
    }
    private:
    int a;
    char *str;
    };

    int main()
    {
    CA A(10,"Hello!");
    CA B=A;
    B.Show();
    return 0;
    }

    深拷貝和淺拷貝的定義可以簡(jiǎn)單理解成:如果一個(gè)類(lèi)擁有資源(堆,或者是其它系統(tǒng)資源),當(dāng)這個(gè)類(lèi)的對(duì)象發(fā)生復(fù)制過(guò)程的時(shí)候,這個(gè)過(guò)程就可以叫做深拷貝,反之對(duì)象存在資源,但復(fù)制過(guò)程并未復(fù)制資源的情況視為淺拷貝。

    淺拷貝資源后在釋放資源的時(shí)候會(huì)產(chǎn)生資源歸屬不清的情況導(dǎo)致程序運(yùn)行出錯(cuò)。

            Test(Test &c_t)是自定義的拷貝構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)的名稱(chēng)必須與類(lèi)名稱(chēng)一致,函數(shù)的形式參數(shù)是本類(lèi)型的一個(gè)引用變量,且必須是引用。

    當(dāng)用一個(gè)已經(jīng)初始化過(guò)了的自定義類(lèi)類(lèi)型對(duì)象去初始化另一個(gè)新構(gòu)造的對(duì)象的時(shí)候,拷貝構(gòu)造函數(shù)就會(huì)被自動(dòng)調(diào)用,如果你沒(méi)有自定義拷貝構(gòu)造函數(shù)的時(shí)候,系統(tǒng)將會(huì)提供給一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù)來(lái)完成這個(gè)過(guò)程,上面代碼的復(fù)制核心語(yǔ)句就是通過(guò)Test(Test &c_t)拷貝構(gòu)造函數(shù)內(nèi)的p1=c_t.p1;語(yǔ)句完成的。

     

     


    關(guān)于C語(yǔ)言的void main() ---轉(zhuǎn)帖
    閱讀:293回復(fù):0
    樓主#
    發(fā)表于 2012-8-10 22:55:46
            很多人甚至市面上的一些書(shū)籍,都使用了void main( ),其實(shí)這是錯(cuò)誤的。C/C++中從來(lái)沒(méi)有定義過(guò)void main( )。C++之父Bjarne Stroustrup在他的主頁(yè)上的FAQ中明確地寫(xiě)著The definition void main( ) { /* ... */ } is not and never has been C++, nor has it even been C.( void main( )從來(lái)就不存在于C++或者C)。下面我分別說(shuō)一下C和C++標(biāo)準(zhǔn)中對(duì)main函數(shù)的定義。

    一、 C語(yǔ)言中的main() 在C89中,main( )是可以接受的。Brian W. Kernighan和Dennis M. Ritchie的經(jīng)典巨著The C programming Language 2e(《C 程序設(shè)計(jì)語(yǔ)言第二版》)用的就是main( )。不過(guò)在最新的C99標(biāo)準(zhǔn)中,只有以下兩種定義方式是正確的:

    int main(void)

    int main(int argc, char *argv[])

    (參考資料:ISO/IEC 9899:1999 (E) Programming languages ? C 5.1.2.2.1 Program startup)

    當(dāng)然,我們也可以做一點(diǎn)小小的改動(dòng)。例如:char *argv[]可以寫(xiě)成char **argv;argv和argc可以改成別的變量名(如intval和charval),不過(guò)一定要符合變量的命名規(guī)則。

    如果不需要從命令行中獲取參數(shù),請(qǐng)用int main(void);否則請(qǐng)用int main(int argc, char *argv[])。

    main函數(shù)的返回值類(lèi)型必須是int,這樣返回值才能傳遞給程序的調(diào)用者(如操作系統(tǒng))。

    如果main函數(shù)的最后沒(méi)有寫(xiě)return語(yǔ)句的話(huà),C99規(guī)定編譯器要自動(dòng)在生成的目標(biāo)文件中(如exe文件)加入return 0;,表示程序正常退出。不過(guò),我還是建議你最好在main函數(shù)的最后加上return語(yǔ)句,雖然沒(méi)有這個(gè)必要,但這是一個(gè)好的習(xí)慣。注意,vc6不會(huì)在目標(biāo)文件中加入return 0;,大概是因?yàn)関c6是98年的產(chǎn)品,所以才不支持這個(gè)特性?,F(xiàn)在明白我為什么建議你最好加上return語(yǔ)句了吧!不過(guò),gcc3.2(Linux下的C編譯器)會(huì)在生成的目標(biāo)文件中加入return 0;。
    二、 C++中的main() C++98中定義了如下兩種main函數(shù)的定義方式:

    int main( )

    int main(int argc, char *argv[])

    參考資料:ISO/IEC 14882(1998-9-01)Programming languages ? C++ 3.6 Start and termination

    int main( )等同于C99中的int main(void);int main(int argc, char *argv[])的用法也和C99中定義的一樣。同樣,main函數(shù)的返回值類(lèi)型也必須是int。如果main函數(shù)的末尾沒(méi)寫(xiě)return語(yǔ)句,C++98規(guī)定編譯器要自動(dòng)在生成的目標(biāo)文件中加入return 0;。同樣,vc6也不支持這個(gè)特性,但是g++3.2(Linux下的C++編譯器)支持。
    三、 關(guān)于void main() 在C和C++中,不接收任何參數(shù)也不返回任何信息的函數(shù)原型為“void foo(void);”。可能正是因?yàn)檫@個(gè),所以很多人都誤認(rèn)為如果不需要程序返回值時(shí)可以把main函數(shù)定義成void main(void)。然而這是錯(cuò)誤的!main函數(shù)的返回值應(yīng)該定義為int類(lèi)型,C和C++標(biāo)準(zhǔn)中都是這樣規(guī)定的。雖然在一些編譯器中,void main可以通過(guò)編譯(如vc6),但并非所有編譯器都支持void main,因?yàn)闃?biāo)準(zhǔn)中從來(lái)沒(méi)有定義過(guò)void main。g++3.2中如果main函數(shù)的返回值不是int類(lèi)型,就根本通不過(guò)編譯。而gcc3.2則會(huì)發(fā)出警告。所以,如果你想你的程序擁有很好的可移植性,請(qǐng)一定要用int main。

    不要用“我的老師告訴我這么做是對(duì)的”之類(lèi)的話(huà)來(lái)為自己開(kāi)脫;老師們總是習(xí)慣犯錯(cuò)誤(teachers have a bad habit of being wrong)。寫(xiě)安全的,合乎標(biāo)準(zhǔn)的代碼,大家就可以專(zhuān)注于你程序中其它的問(wèn)題而不是在這種規(guī)范方面的東西上浪費(fèi)時(shí)間。

    應(yīng)當(dāng)指出:在某些系統(tǒng)中,若程序使用void main定義或沒(méi)有return值,則可能導(dǎo)致堆棧異常從而導(dǎo)致系統(tǒng)故障。(詳見(jiàn)后面英文部分)
    四、返回值的作用 main函數(shù)的返回值用于說(shuō)明程序的退出狀態(tài)。如果返回0,則代表程序正常退出;返回其它數(shù)字的含義則由系統(tǒng)決定。通常,返回非零代表程序異常退出。下面我們?cè)趙inxp環(huán)境下做一個(gè)小實(shí)驗(yàn)。首先編譯下面的程序:

    int main(void)

    {

    return 0;

    }

    然后打開(kāi)附件里的“命令提示符”,在命令行里運(yùn)行剛才編譯好的可執(zhí)行文件,然后輸入“echo %ERRORLEVEL%”,回車(chē),就可以看到程序的返回值為0。假設(shè)剛才編譯好的文件是a.exe,如果輸入“a && dir”,則會(huì)列出當(dāng)前目錄下的文件夾和文件。但是如果改成“return -1”,或者別的非0值,重新編譯后輸入“a && dir”,則dir不會(huì)執(zhí)行。因?yàn)?amp;&的含義是:如果&&前面的程序正常退出,則繼續(xù)執(zhí)行&&后面的程序,否則不執(zhí)行。也就是說(shuō),利用程序的返回值,我們可以控制要不要執(zhí)行下一個(gè)程序。這就是int main的好處。如果你有興趣,也可以把main函數(shù)的返回值類(lèi)型改成非int類(lèi)型(如float),重新編譯后執(zhí)行“a && dir”,看看會(huì)出現(xiàn)什么情況,想想為什么會(huì)出現(xiàn)那樣的情況。順便提一下,如果輸入a || dir的話(huà),則表示如果a異常退出,則執(zhí)行dir。
    五、那么int main(int argc, char *argv[], char *envp[])呢? 這當(dāng)然也不是標(biāo)準(zhǔn)C/C++里面定義的東西!char *envp[]是某些編譯器提供的擴(kuò)展功能,用于獲取系統(tǒng)的環(huán)境變量。因?yàn)椴皇菢?biāo)準(zhǔn),所以并非所有編譯器都支持,故而移植性差,不推薦使用——除非你的程序是專(zhuān)門(mén)設(shè)計(jì)用于工作在特定的環(huán)境中而且需要獲取系統(tǒng)的環(huán)境變量。


                                                                                                     ----轉(zhuǎn)《C語(yǔ)言中文網(wǎng)》

     

     

     

     


    關(guān)于C語(yǔ)言的void main() ---轉(zhuǎn)帖
    閱讀:293回復(fù):0
    樓主#
    發(fā)表于 2012-8-10 22:55:46
            很多人甚至市面上的一些書(shū)籍,都使用了void main( ),其實(shí)這是錯(cuò)誤的。C/C++中從來(lái)沒(méi)有定義過(guò)void main( )。C++之父Bjarne Stroustrup在他的主頁(yè)上的FAQ中明確地寫(xiě)著The definition void main( ) { /* ... */ } is not and never has been C++, nor has it even been C.( void main( )從來(lái)就不存在于C++或者C)。下面我分別說(shuō)一下C和C++標(biāo)準(zhǔn)中對(duì)main函數(shù)的定義。

    一、 C語(yǔ)言中的main() 在C89中,main( )是可以接受的。Brian W. Kernighan和Dennis M. Ritchie的經(jīng)典巨著The C programming Language 2e(《C 程序設(shè)計(jì)語(yǔ)言第二版》)用的就是main( )。不過(guò)在最新的C99標(biāo)準(zhǔn)中,只有以下兩種定義方式是正確的:

    int main(void)

    int main(int argc, char *argv[])

    (參考資料:ISO/IEC 9899:1999 (E) Programming languages ? C 5.1.2.2.1 Program startup)

    當(dāng)然,我們也可以做一點(diǎn)小小的改動(dòng)。例如:char *argv[]可以寫(xiě)成char **argv;argv和argc可以改成別的變量名(如intval和charval),不過(guò)一定要符合變量的命名規(guī)則。

    如果不需要從命令行中獲取參數(shù),請(qǐng)用int main(void);否則請(qǐng)用int main(int argc, char *argv[])。

    main函數(shù)的返回值類(lèi)型必須是int,這樣返回值才能傳遞給程序的調(diào)用者(如操作系統(tǒng))。

    如果main函數(shù)的最后沒(méi)有寫(xiě)return語(yǔ)句的話(huà),C99規(guī)定編譯器要自動(dòng)在生成的目標(biāo)文件中(如exe文件)加入return 0;,表示程序正常退出。不過(guò),我還是建議你最好在main函數(shù)的最后加上return語(yǔ)句,雖然沒(méi)有這個(gè)必要,但這是一個(gè)好的習(xí)慣。注意,vc6不會(huì)在目標(biāo)文件中加入return 0;,大概是因?yàn)関c6是98年的產(chǎn)品,所以才不支持這個(gè)特性?,F(xiàn)在明白我為什么建議你最好加上return語(yǔ)句了吧!不過(guò),gcc3.2(Linux下的C編譯器)會(huì)在生成的目標(biāo)文件中加入return 0;。
    二、 C++中的main() C++98中定義了如下兩種main函數(shù)的定義方式:

    int main( )

    int main(int argc, char *argv[])

    參考資料:ISO/IEC 14882(1998-9-01)Programming languages ? C++ 3.6 Start and termination

    int main( )等同于C99中的int main(void);int main(int argc, char *argv[])的用法也和C99中定義的一樣。同樣,main函數(shù)的返回值類(lèi)型也必須是int。如果main函數(shù)的末尾沒(méi)寫(xiě)return語(yǔ)句,C++98規(guī)定編譯器要自動(dòng)在生成的目標(biāo)文件中加入return 0;。同樣,vc6也不支持這個(gè)特性,但是g++3.2(Linux下的C++編譯器)支持。
    三、 關(guān)于void main() 在C和C++中,不接收任何參數(shù)也不返回任何信息的函數(shù)原型為“void foo(void);”??赡苷且?yàn)檫@個(gè),所以很多人都誤認(rèn)為如果不需要程序返回值時(shí)可以把main函數(shù)定義成void main(void)。然而這是錯(cuò)誤的!main函數(shù)的返回值應(yīng)該定義為int類(lèi)型,C和C++標(biāo)準(zhǔn)中都是這樣規(guī)定的。雖然在一些編譯器中,void main可以通過(guò)編譯(如vc6),但并非所有編譯器都支持void main,因?yàn)闃?biāo)準(zhǔn)中從來(lái)沒(méi)有定義過(guò)void main。g++3.2中如果main函數(shù)的返回值不是int類(lèi)型,就根本通不過(guò)編譯。而gcc3.2則會(huì)發(fā)出警告。所以,如果你想你的程序擁有很好的可移植性,請(qǐng)一定要用int main。

    不要用“我的老師告訴我這么做是對(duì)的”之類(lèi)的話(huà)來(lái)為自己開(kāi)脫;老師們總是習(xí)慣犯錯(cuò)誤(teachers have a bad habit of being wrong)。寫(xiě)安全的,合乎標(biāo)準(zhǔn)的代碼,大家就可以專(zhuān)注于你程序中其它的問(wèn)題而不是在這種規(guī)范方面的東西上浪費(fèi)時(shí)間。

    應(yīng)當(dāng)指出:在某些系統(tǒng)中,若程序使用void main定義或沒(méi)有return值,則可能導(dǎo)致堆棧異常從而導(dǎo)致系統(tǒng)故障。(詳見(jiàn)后面英文部分)
    四、返回值的作用 main函數(shù)的返回值用于說(shuō)明程序的退出狀態(tài)。如果返回0,則代表程序正常退出;返回其它數(shù)字的含義則由系統(tǒng)決定。通常,返回非零代表程序異常退出。下面我們?cè)趙inxp環(huán)境下做一個(gè)小實(shí)驗(yàn)。首先編譯下面的程序:

    int main(void)

    {

    return 0;

    }

    然后打開(kāi)附件里的“命令提示符”,在命令行里運(yùn)行剛才編譯好的可執(zhí)行文件,然后輸入“echo %ERRORLEVEL%”,回車(chē),就可以看到程序的返回值為0。假設(shè)剛才編譯好的文件是a.exe,如果輸入“a && dir”,則會(huì)列出當(dāng)前目錄下的文件夾和文件。但是如果改成“return -1”,或者別的非0值,重新編譯后輸入“a && dir”,則dir不會(huì)執(zhí)行。因?yàn)?amp;&的含義是:如果&&前面的程序正常退出,則繼續(xù)執(zhí)行&&后面的程序,否則不執(zhí)行。也就是說(shuō),利用程序的返回值,我們可以控制要不要執(zhí)行下一個(gè)程序。這就是int main的好處。如果你有興趣,也可以把main函數(shù)的返回值類(lèi)型改成非int類(lèi)型(如float),重新編譯后執(zhí)行“a && dir”,看看會(huì)出現(xiàn)什么情況,想想為什么會(huì)出現(xiàn)那樣的情況。順便提一下,如果輸入a || dir的話(huà),則表示如果a異常退出,則執(zhí)行dir。
    五、那么int main(int argc, char *argv[], char *envp[])呢? 這當(dāng)然也不是標(biāo)準(zhǔn)C/C++里面定義的東西!char *envp[]是某些編譯器提供的擴(kuò)展功能,用于獲取系統(tǒng)的環(huán)境變量。因?yàn)椴皇菢?biāo)準(zhǔn),所以并非所有編譯器都支持,故而移植性差,不推薦使用——除非你的程序是專(zhuān)門(mén)設(shè)計(jì)用于工作在特定的環(huán)境中而且需要獲取系統(tǒng)的環(huán)境變量。


                                                                                                     ----轉(zhuǎn)《C語(yǔ)言中文網(wǎng)》

     

     

     

     

     

     

     

     

     


    C/C++語(yǔ)言void及void指針深層探索(轉(zhuǎn))     發(fā)布時(shí)間:2008-11-24 15:56:07
    技術(shù)類(lèi)別:軟件開(kāi)發(fā)   
    1.概述
    許多初學(xué)者對(duì)C/C++語(yǔ)言中的void及void指針類(lèi)型不甚理解,因此在使用上出現(xiàn)了一些錯(cuò)誤。本文將對(duì)void關(guān)鍵字的深刻含義進(jìn)行解說(shuō),并詳述void及void指針類(lèi)型的使用方法與技巧。

    2.void的含義
    void的字面意思是“無(wú)類(lèi)型”,void *則為“無(wú)類(lèi)型指針”,void *可以指向任何類(lèi)型的數(shù)據(jù)。

    void幾乎只有“注釋”和限制程序的作用,因?yàn)閺膩?lái)沒(méi)有人會(huì)定義一個(gè)void變量,讓我們?cè)囍鴣?lái)定義:

    void a;

    這行語(yǔ)句編譯時(shí)會(huì)出錯(cuò),提示“illegal use of type 'void'”。不過(guò),即使void a的編譯不會(huì)出錯(cuò),它也沒(méi)有任何實(shí)際意義。

    void真正發(fā)揮的作用在于:
    (1) 對(duì)函數(shù)返回的限定;
    (2) 對(duì)函數(shù)參數(shù)的限定。

    我們將在第三節(jié)對(duì)以上二點(diǎn)進(jìn)行具體說(shuō)明。

    眾所周知,如果指針p1和p2的類(lèi)型相同,那么我們可以直接在p1和p2間互相賦值;如果p1和p2指向不同的數(shù)據(jù)類(lèi)型,則必須使用強(qiáng)制類(lèi)型轉(zhuǎn)換運(yùn)算符把賦值運(yùn)算符右邊的指針類(lèi)型轉(zhuǎn)換為左邊指針的類(lèi)型。

    例如:

    float *p1;
    int *p2;
    p1 = p2;

    其中p1 = p2語(yǔ)句會(huì)編譯出錯(cuò),提示“'=' : cannot convert from 'int *' to 'float *'”,必須改為:

    p1 = (float *)p2;

    而void *則不同,任何類(lèi)型的指針都可以直接賦值給它,無(wú)需進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換:

    void *p1;
    int *p2;
    p1 = p2;


    但這并不意味著,void *也可以無(wú)需強(qiáng)制類(lèi)型轉(zhuǎn)換地賦給其它類(lèi)型的指針。因?yàn)椤盁o(wú)類(lèi)型”可以包容“有類(lèi)型”,而“有類(lèi)型”則不能包容“無(wú)類(lèi)型”。道理很簡(jiǎn)單,我們可以說(shuō)“男人和女人都是人”,但不能說(shuō)“人是男人”或者“人是女人”。下面的語(yǔ)句編譯出錯(cuò):

    void *p1;
    int *p2;
    p2 = p1;


    提示“'=' : cannot convert from 'void *' to 'int *'”。

    3.void的使用

    下面給出void關(guān)鍵字的使用規(guī)則:
    規(guī)則一 如果函數(shù)沒(méi)有返回值,那么應(yīng)聲明為void類(lèi)型

    在C語(yǔ)言中,凡不加返回值類(lèi)型限定的函數(shù),就會(huì)被編譯器作為返回整型值處理。但是許多程序員卻誤以為其為void類(lèi)型。例如:

    add ( int a, int b )
    {
    return a + b;
    }
    int main(int argc, char* argv[])
    {
    printf ( "2 + 3 = %d", add ( 2, 3) );
    }


    程序運(yùn)行的結(jié)果為輸出:
    2 + 3 = 5
    這說(shuō)明不加返回值說(shuō)明的函數(shù)的確為int函數(shù)。

    林銳博士《高質(zhì)量C/C++編程》中提到:“C++語(yǔ)言有很?chē)?yán)格的類(lèi)型安全檢查,不允許上述情況(指函數(shù)不加類(lèi)型聲明)發(fā)生”??墒蔷幾g器并不一定這么認(rèn)定,譬如在Visual C++6.0中上述add函數(shù)的編譯無(wú)錯(cuò)也無(wú)警告且運(yùn)行正確,所以不能寄希望于編譯器會(huì)做嚴(yán)格的類(lèi)型檢查。

    因此,為了避免混亂,我們?cè)诰帉?xiě)C/C++程序時(shí),對(duì)于任何函數(shù)都必須一個(gè)不漏地指定其類(lèi)型。如果函數(shù)沒(méi)有返回值,一定要聲明為void類(lèi)型。這既是程序良好可讀性的需要,也是編程規(guī)范性的要求。另外,加上void類(lèi)型聲明后,也可以發(fā)揮代碼的“自注釋”作用。代碼的“自注釋”即代碼能自己注釋自己。
    規(guī)則二 如果函數(shù)無(wú)參數(shù),那么應(yīng)聲明其參數(shù)為void

    在C++語(yǔ)言中聲明一個(gè)這樣的函數(shù):

    int function(void)
    {
    return 1;
    }


    則進(jìn)行下面的調(diào)用是不合法的:

    function(2);


    因?yàn)樵贑++中,函數(shù)參數(shù)為void的意思是這個(gè)函數(shù)不接受任何參數(shù)。

    我們?cè)赥urbo C 2.0中編譯:

    #include "stdio.h"
    fun()
    {
    return 1;
    }
    main()
    {
    printf("%d",fun(2));
    getchar();
    }


    編譯正確且輸出1,這說(shuō)明,在C語(yǔ)言中,可以給無(wú)參數(shù)的函數(shù)傳送任意類(lèi)型的參數(shù),但是在C++編譯器中編譯同樣的代碼則會(huì)出錯(cuò)。在C++中,不能向無(wú)參數(shù)的函數(shù)傳送任何參數(shù),出錯(cuò)提示“'fun' : function does not take 1 parameters”。

    所以,無(wú)論在C還是C++中,若函數(shù)不接受任何參數(shù),一定要指明參數(shù)為void。

    規(guī)則三 小心使用void指針類(lèi)型

    按照ANSI(American National Standards Institute)標(biāo)準(zhǔn),不能對(duì)void指針進(jìn)行算法操作,即下列操作都是不合法的:

    void * pvoid;
    pvoid++; //ANSI:錯(cuò)誤
    pvoid += 1; //ANSI:錯(cuò)誤
    //ANSI標(biāo)準(zhǔn)之所以這樣認(rèn)定,是因?yàn)樗鼒?jiān)持:進(jìn)行算法操作的指針必須是確定知道其指向數(shù)據(jù)類(lèi)型大小的。
    //例如:
    int *pint;
    pint++; //ANSI:正確


    pint++的結(jié)果是使其增大sizeof(int)。

    但是大名鼎鼎的GNU(GNU's Not Unix的縮寫(xiě))則不這么認(rèn)定,它指定void *的算法操作與char *一致。
    因此下列語(yǔ)句在GNU編譯器中皆正確:

    pvoid++; //GNU:正確
    pvoid += 1; //GNU:正確


    pvoid++的執(zhí)行結(jié)果是其增大了1。

    在實(shí)際的程序設(shè)計(jì)中,為迎合ANSI標(biāo)準(zhǔn),并提高程序的可移植性,我們可以這樣編寫(xiě)實(shí)現(xiàn)同樣功能的代碼:

    void * pvoid;
    (char *)pvoid++; //ANSI:正確;GNU:正確
    (char *)pvoid += 1; //ANSI:錯(cuò)誤;GNU:正確


    GNU和ANSI還有一些區(qū)別,總體而言,GNU較ANSI更“開(kāi)放”,提供了對(duì)更多語(yǔ)法的支持。但是我們?cè)谡鎸?shí)設(shè)計(jì)時(shí),還是應(yīng)該盡可能地迎合ANSI標(biāo)準(zhǔn)。

    規(guī)則四 如果函數(shù)的參數(shù)可以是任意類(lèi)型指針,那么應(yīng)聲明其參數(shù)為void *

    典型的如內(nèi)存操作函數(shù)memcpy和memset的函數(shù)原型分別為:

    void * memcpy(void *dest, const void *src, size_t len);
    void * memset ( void * buffer, int c, size_t num );


    這樣,任何類(lèi)型的指針都可以傳入memcpy和memset中,這也真實(shí)地體現(xiàn)了內(nèi)存操作函數(shù)的意義,因?yàn)樗僮鞯膶?duì)象僅僅是一片內(nèi)存,而不論這片內(nèi)存是什么類(lèi)型。如果memcpy和memset的參數(shù)類(lèi)型不是void *,而是char *,那才叫真的奇怪了!這樣的memcpy和memset明顯不是一個(gè)“純粹的,脫離低級(jí)趣味的”函數(shù)!

    下面的代碼執(zhí)行正確:

    //示例:memset接受任意類(lèi)型指針
    int intarray[100];
    memset ( intarray, 0, 100*sizeof(int) ); //將intarray清0

    //示例:memcpy接受任意類(lèi)型指針
    int intarray1[100], intarray2[100];
    memcpy ( intarray1, intarray2, 100*sizeof(int) ); //將intarray2拷貝給intarray1


    有趣的是,memcpy和memset函數(shù)返回的也是void *類(lèi)型,標(biāo)準(zhǔn)庫(kù)函數(shù)的編寫(xiě)者是多么地富有學(xué)問(wèn)啊!

    規(guī)則五 void不能代表一個(gè)真實(shí)的變量

    下面代碼都企圖讓void代表一個(gè)真實(shí)的變量,因此都是錯(cuò)誤的代碼:

    void a; //錯(cuò)誤
    function(void a); //錯(cuò)誤


    void體現(xiàn)了一種抽象,這個(gè)世界上的變量都是“有類(lèi)型”的,譬如一個(gè)人不是男人就是女人(還有人妖?)。

    void的出現(xiàn)只是為了一種抽象的需要,如果你正確地理解了面向?qū)ο笾小俺橄蠡?lèi)”的概念,也很容易理解void數(shù)據(jù)類(lèi)型。正如不能給抽象基類(lèi)定義一個(gè)實(shí)例,我們也不能定義一個(gè)void(讓我們類(lèi)比的稱(chēng)void為“抽象數(shù)據(jù)類(lèi)型”)變量。

    4.總結(jié)
    小小的void蘊(yùn)藏著很豐富的設(shè)計(jì)哲學(xué),作為一名程序設(shè)計(jì)人員,對(duì)問(wèn)題進(jìn)行深一個(gè)層次的思考必然使我們受益匪淺。

     

     

     

     

    C++拷貝構(gòu)造函數(shù)(深拷貝,淺拷貝)

    對(duì)于普通類(lèi)型的對(duì)象來(lái)說(shuō),它們之間的復(fù)制是很簡(jiǎn)單的,例如:
    int a=88;
    int b=a;
    而類(lèi)對(duì)象與普通對(duì)象不同,類(lèi)對(duì)象內(nèi)部結(jié)構(gòu)一般較為復(fù)雜,存在各種成員變量。下面看一個(gè)類(lèi)對(duì)象拷貝的簡(jiǎn)單例子。

    #include <iostream>
    using namespace std;

    class CExample {
    private:
        int a;
    public:
        CExample(int b)
        { a=b;}
        void Show ()
        {
            cout<<a<<endl;
        }
    };

    int main()
    {
        CExample A(100);
        CExample B=A;
        B.Show ();
        return 0;
    }
    運(yùn)行程序,屏幕輸出100。從以上代碼的運(yùn)行結(jié)果可以看出,系統(tǒng)為對(duì)象B分配了內(nèi)存并完成了與對(duì)象A的復(fù)制過(guò)程。就類(lèi)對(duì)象而言,相同類(lèi)型的類(lèi)對(duì)象是通過(guò)拷貝構(gòu)造函數(shù)來(lái)完成整個(gè)復(fù)制過(guò)程的。下面舉例說(shuō)明拷貝構(gòu)造函數(shù)的工作過(guò)程。

    #include <iostream>
    using namespace std;

    class CExample {
    private:
        int a;
    public:
        CExample(int b)
        { a=b;}
       
        CExample(const CExample& C)
        {
            a=C.a;
        }
        void Show ()
        {
            cout<<a<<endl;
        }
    };

    int main()
    {
        CExample A(100);
        CExample B=A;
        B.Show ();
        return 0;
    }
    CExample(const CExample& C)就是我們自定義的拷貝構(gòu)造函數(shù)??梢?jiàn),拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),函數(shù)的名稱(chēng)必須和類(lèi)名稱(chēng)一致,它的唯一的一個(gè)參數(shù)是本類(lèi)型的一個(gè)引用變量,該參數(shù)是const類(lèi)型,不可變的。例如:類(lèi)X的拷貝構(gòu)造函數(shù)的形式為X(X& x)。

    當(dāng)用一個(gè)已初始化過(guò)了的自定義類(lèi)類(lèi)型對(duì)象去初始化另一個(gè)新構(gòu)造的對(duì)象的時(shí)候,拷貝構(gòu)造函數(shù)就會(huì)被自動(dòng)調(diào)用。也就是說(shuō),當(dāng)類(lèi)的對(duì)象需要拷貝時(shí),拷貝構(gòu)造函數(shù)將會(huì)被調(diào)用。以下情況都會(huì)調(diào)用拷貝構(gòu)造函數(shù):
    一個(gè)對(duì)象以值傳遞的方式傳入函數(shù)體
    一個(gè)對(duì)象以值傳遞的方式從函數(shù)返回
    一個(gè)對(duì)象需要通過(guò)另外一個(gè)對(duì)象進(jìn)行初始化。

    如果在類(lèi)中沒(méi)有顯式地聲明一個(gè)拷貝構(gòu)造函數(shù),那么,編譯器將會(huì)自動(dòng)生成一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù),該構(gòu)造函數(shù)完成對(duì)象之間的位拷貝。位拷貝又稱(chēng)淺拷貝,后面將進(jìn)行說(shuō)明。

    自定義拷貝構(gòu)造函數(shù)是一種良好的編程風(fēng)格,它可以阻止編譯器形成默認(rèn)的拷貝構(gòu)造函數(shù),提高源碼效率。

    淺拷貝和深拷貝

    在某些狀況下,類(lèi)內(nèi)成員變量需要?jiǎng)討B(tài)開(kāi)辟堆內(nèi)存,如果實(shí)行位拷貝,也就是把對(duì)象里的值完全復(fù)制給另一個(gè)對(duì)象,如A=B。這時(shí),如果B中有一個(gè)成員變量指針已經(jīng)申請(qǐng)了內(nèi)存,那A中的那個(gè)成員變量也指向同一塊內(nèi)存。這就出現(xiàn)了問(wèn)題:當(dāng)B把內(nèi)存釋放了(如:析構(gòu)),這時(shí)A內(nèi)的指針就是野指針了,出現(xiàn)運(yùn)行錯(cuò)誤。

    深拷貝和淺拷貝可以簡(jiǎn)單理解為:如果一個(gè)類(lèi)擁有資源,當(dāng)這個(gè)類(lèi)的對(duì)象發(fā)生復(fù)制過(guò)程的時(shí)候,資源重新分配,這個(gè)過(guò)程就是深拷貝,反之,沒(méi)有重新分配資源,就是淺拷貝。下面舉個(gè)深拷貝的例子。

    #include <iostream>
    using namespace std;
    class CA
    {
    public:
    CA(int b,char* cstr)
    {
    a=b;
    str=new char[b];
    strcpy(str,cstr);
    }
    CA(const CA& C)
    {
    a=C.a;
    str=new char[a]; //深拷貝
    if(str!=0)
    strcpy(str,C.str);
    }
    void Show()
    {
    cout<<str<<endl;
    }
    ~CA()
    {
    delete str;
    }
    private:
    int a;
    char *str;
    };

    int main()
    {
    CA A(10,"Hello!");
    CA B=A;
    B.Show();
    return 0;
    }

    深拷貝和淺拷貝的定義可以簡(jiǎn)單理解成:如果一個(gè)類(lèi)擁有資源(堆,或者是其它系統(tǒng)資源),當(dāng)這個(gè)類(lèi)的對(duì)象發(fā)生復(fù)制過(guò)程的時(shí)候,這個(gè)過(guò)程就可以叫做深拷貝,反之對(duì)象存在資源,但復(fù)制過(guò)程并未復(fù)制資源的情況視為淺拷貝。

    淺拷貝資源后在釋放資源的時(shí)候會(huì)產(chǎn)生資源歸屬不清的情況導(dǎo)致程序運(yùn)行出錯(cuò)。

            Test(Test &c_t)是自定義的拷貝構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)的名稱(chēng)必須與類(lèi)名稱(chēng)一致,函數(shù)的形式參數(shù)是本類(lèi)型的一個(gè)引用變量,且必須是引用。

    當(dāng)用一個(gè)已經(jīng)初始化過(guò)了的自定義類(lèi)類(lèi)型對(duì)象去初始化另一個(gè)新構(gòu)造的對(duì)象的時(shí)候,拷貝構(gòu)造函數(shù)就會(huì)被自動(dòng)調(diào)用,如果你沒(méi)有自定義拷貝構(gòu)造函數(shù)的時(shí)候,系統(tǒng)將會(huì)提供給一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù)來(lái)完成這個(gè)過(guò)程,上面代碼的復(fù)制核心語(yǔ)句就是通過(guò)Test(Test &c_t)拷貝構(gòu)造函數(shù)內(nèi)的p1=c_t.p1;語(yǔ)句完成的。

    分類(lèi): Basic C++
    標(biāo)簽: 拷貝構(gòu)造函數(shù), 深拷貝, 淺拷貝

     

     

     

     

     

     

     

     

     

     

     

     

     


    typedef

    百科名片

     
    typedef struc
    在計(jì)算機(jī)編程語(yǔ)言中用來(lái)為復(fù)雜的聲明定義簡(jiǎn)單的別名,與宏定義有些差異。它本身是一種存儲(chǔ)類(lèi)的關(guān)鍵字,與auto、extern、mutable、static、register等關(guān)鍵字不能出現(xiàn)在同一個(gè)表達(dá)式中。
    目錄

    定義
    用法總結(jié)
    語(yǔ)言用法
    代碼簡(jiǎn)化
    平臺(tái)開(kāi)發(fā)
    編輯本段
    定義

    typedef聲明,簡(jiǎn)稱(chēng)typedef,為現(xiàn)有類(lèi)型創(chuàng)建一個(gè)新的名字,或稱(chēng)為類(lèi)型別名,在結(jié)構(gòu)體定義,還有一些數(shù)組等地方都大量的用到。
    它有助于創(chuàng)建平臺(tái)無(wú)關(guān)類(lèi)型,甚至能隱藏復(fù)雜和難以理解的語(yǔ)法 。使用typedef可編寫(xiě)出更加美觀和可讀的代碼。所謂美觀,意指typedef能隱藏笨拙的語(yǔ)法構(gòu)造以及平臺(tái)相關(guān)的數(shù)據(jù)類(lèi)型,從而增強(qiáng)可移植性以及未來(lái)的可維護(hù)性。本文下面將竭盡全力來(lái)揭示typedef強(qiáng)大功能以及如何避免一些常見(jiàn)的使用陷阱。[1]
    編輯本段
    用法總結(jié)

    如何創(chuàng)建平臺(tái)無(wú)關(guān)的數(shù)據(jù)類(lèi)型,隱藏笨拙且難以理解的語(yǔ)法?
    使用typedef為現(xiàn)有類(lèi)型創(chuàng)建同義字,定義易于記憶的類(lèi)型名
    typedef使用最多的地方是創(chuàng)建易于記憶的類(lèi)型名,用它來(lái)歸檔程序員的意圖。類(lèi)型出現(xiàn)在所聲明的變量名字中,位于“typedef”關(guān)鍵字右邊。例如:
    typedef int size;
    此聲明定義了一個(gè)int的同義字,名字為size。注意typedef并不創(chuàng)建新的類(lèi)型。它僅僅為現(xiàn)有類(lèi)型添加一個(gè)同義字。你可以在任何需要int的上下文中使用size:
    void measure(size * psz);
    size array[4];
    size len = file.getlength();
    std::vector<size> vs;
    typedef 還可以掩飾復(fù)合類(lèi)型,如指針和數(shù)組。
    例如,你不用像下面這樣重復(fù)定義有 81 個(gè)字符元素的數(shù)組:
    char line[81];
    char text[81];
    定義一個(gè) typedef,每當(dāng)要用到相同類(lèi)型和大小的數(shù)組時(shí),可以這樣:
    typedef char Line[81];
    此時(shí)Line類(lèi)型即代表了具有81個(gè)元素的字符數(shù)組,使用方法如下:
    Line text, secondline;
    getline(text);
    同樣,可以像下面這樣隱藏指針語(yǔ)法:
    typedef char * pstr;
    int mystrcmp(pstr, pstr);
    這里將帶我們到達(dá)第一個(gè) typedef 陷阱。標(biāo)準(zhǔn)函數(shù) strcmp()有兩個(gè)‘ const char *'類(lèi)型的參數(shù)。因此,它可能會(huì)誤導(dǎo)人們像下面這樣聲明 mystrcmp():
    int mystrcmp(const pstr, const pstr);
    用GNU的gcc和g++編譯器,是會(huì)出現(xiàn)警告的,按照順序,‘const pstr'被解釋為‘char* const‘(一個(gè)指向char的指針常量),兩者表達(dá)的并非同一意思(詳見(jiàn)C++ Primer 第四版 P112)。
    char * const cp : 定義一個(gè)指向字符的指針常數(shù),即const指針,常指針。
    const char* p : 定義一個(gè)指向字符常數(shù)的指針,即常量指針。
    char const* p : 等同于const char* p[2]。
    為了得到正確的類(lèi)型,應(yīng)當(dāng)如下聲明:
    typedef const char* pstr;
    編輯本段
    語(yǔ)言用法

    基本解釋
    typedef為C語(yǔ)言的關(guān)鍵字,作用是為一種數(shù)據(jù)類(lèi)型定義一個(gè)新名字。這里的數(shù)據(jù)類(lèi)型包括內(nèi)部數(shù)據(jù)類(lèi)型(int,char等)和自定義的數(shù)據(jù)類(lèi)型(struct等)。
    在編程中使用typedef目的一般有兩個(gè),一個(gè)是給變量一個(gè)易記且意義明確的新名字,另一個(gè)是簡(jiǎn)化一些比較復(fù)雜的類(lèi)型聲明。
    至于typedef有什么微妙之處,請(qǐng)你接著看下面對(duì)幾個(gè)問(wèn)題的具體闡述。
    2. typedef & 結(jié)構(gòu)的問(wèn)題
    當(dāng)用下面的代碼定義一個(gè)結(jié)構(gòu)時(shí),編譯器報(bào)了一個(gè)錯(cuò)誤,為什么呢?莫非C語(yǔ)言不允許在結(jié)構(gòu)中包含指向它自己的指針嗎?請(qǐng)你先猜想一下,然后看下文說(shuō)明:
    typedef struct tagNode
    {
    char *pItem;
    pNode pNext;
    } *pNode;
    分析:
    1、typedef的最簡(jiǎn)單使用
    typedef long byte_4;
    給已知數(shù)據(jù)類(lèi)型long起個(gè)新名字,叫byte_4。
    2、 typedef與結(jié)構(gòu)結(jié)合使用
    typedef struct tagMyStruct
    {
    int iNum;
    long lLength;
    } MyStruct;
    這語(yǔ)句實(shí)際上完成兩個(gè)操作:
    1) 定義一個(gè)新的結(jié)構(gòu)類(lèi)型
    struct tagMyStruct
    {
    int iNum;
    long lLength;
    };
    分析:tagMyStruct稱(chēng)為“tag”,即“標(biāo)簽”,實(shí)際上是一個(gè)臨時(shí)名字,struct關(guān)鍵字和tagMyStruct一起,構(gòu)成了這個(gè)結(jié)構(gòu)類(lèi)型,不論是否有typedef,這個(gè)結(jié)構(gòu)都存在。
    我們可以用struct tagMyStruct varName來(lái)定義變量,但要注意,使用tagMyStruct varName來(lái)定義變量是不對(duì)的,因?yàn)閟truct 和tagMyStruct合在一起才能表示一個(gè)結(jié)構(gòu)類(lèi)型。
    2) typedef為這個(gè)新的結(jié)構(gòu)起了一個(gè)名字,叫MyStruct。
    typedef struct tagMyStruct MyStruct;
    因此,MyStruct實(shí)際上相當(dāng)于struct tagMyStruct,我們可以使用MyStruct varName來(lái)定義變量。
    答案與分析
    C語(yǔ)言當(dāng)然允許在結(jié)構(gòu)中包含指向它自己的指針,我們可以在建立鏈表等數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)上看到無(wú)數(shù)這樣的例子,上述代碼的根本問(wèn)題在于typedef的應(yīng)用。
    根據(jù)我們上面的闡述可以知道:新結(jié)構(gòu)建立的過(guò)程中遇到了pNext域的聲明,類(lèi)型是pNode,要知道pNode表示的是類(lèi)型的新名字,那么在類(lèi)型本身還沒(méi)有建立完成的時(shí)候,這個(gè)類(lèi)型的新名字也還不存在,也就是說(shuō)這個(gè)時(shí)候編譯器根本不認(rèn)識(shí)pNode。
    解決這個(gè)問(wèn)題的方法有多種:
    1)、
    typedef struct tagNode
    {
    char *pItem;
    struct tagNode *pNext;
    } *pNode;
    2)、
    typedef struct tagNode *pNode;
    struct tagNode
    {
    char *pItem;
    pNode pNext;
    };
    注意:在這個(gè)例子中,你用typedef給一個(gè)還未完全聲明的類(lèi)型起新名字。C語(yǔ)言編譯器支持這種做法。
    3)、規(guī)范做法:
    struct tagNode
    {
    char *pItem;
    struct tagNode *pNext;
    };
    typedef struct tagNode *pNode;
    3. typedef & #define的問(wèn)題
    有下面兩種定義pStr數(shù)據(jù)類(lèi)型的方法,兩者有什么不同?哪一種更好一點(diǎn)?
    typedef char* pStr;
    #define pStr char*;
    答案與分析:
    通常講,typedef要比#define要好,特別是在有指針的場(chǎng)合。請(qǐng)看例子:
    typedef char* pStr1;
    #define pStr2 char *
    pStr1 s1, s2;
    pStr2 s3, s4;
    在上述的變量定義中,s1、s2、s3都被定義為char *,而s4則定義成了char,不是我們所預(yù)期的指針變量,根本原因就在于#define只是簡(jiǎn)單的字符串替換而typedef則是為一個(gè)類(lèi)型起新名字。
    上例中define語(yǔ)句必須寫(xiě)成 pStr2 s3, *s4; 這這樣才能正常執(zhí)行。
    #define用法例子:
    #define f(x) x*x
    main( )
    {
    int a=6,b=2,c;
    c=f(a) / f(b);
    printf("%d \\n",c);
    }
    以下程序的輸出結(jié)果是: 36。
    因?yàn)槿绱嗽颍谠S多C語(yǔ)言編程規(guī)范中提到使用#define定義時(shí),如果定義中包含表達(dá)式,必須使用括號(hào),則上述定義應(yīng)該如下定義才對(duì):
    #define f(x) (x*x)
    當(dāng)然,如果你使用typedef就沒(méi)有這樣的問(wèn)題。
    4. typedef & #define的另一例
    下面的代碼中編譯器會(huì)報(bào)一個(gè)錯(cuò)誤,你知道是哪個(gè)語(yǔ)句錯(cuò)了嗎?
    typedef char * pStr;
    char string[4] = "abc";
    const char *p1 = string;
    const pStr p2 = string;
    p1++;
    p2++;
    答案與分析:
    是p2++出錯(cuò)了。這個(gè)問(wèn)題再一次提醒我們:typedef和#define不同,它不是簡(jiǎn)單的文本替換。上述代碼中const pStr p2并不等于const char * p2。const pStr p2和const long x本質(zhì)上沒(méi)有區(qū)別,都是對(duì)變量進(jìn)行只讀限制,只不過(guò)此處變量p2的數(shù)據(jù)類(lèi)型是我們自己定義的而不是系統(tǒng)固有類(lèi)型而已。因此,const pStr p2的含義是:限定數(shù)據(jù)類(lèi)型為char *的變量p2為只讀,因此p2++錯(cuò)誤。
    #define與typedef引申談
    1) #define宏定義有一個(gè)特別的長(zhǎng)處:可以使用 #ifdef ,#ifndef等來(lái)進(jìn)行邏輯判斷,還可以使用#undef來(lái)取消定義。
    2) typedef也有一個(gè)特別的長(zhǎng)處:它符合范圍規(guī)則,使用typedef定義的變量類(lèi)型其作用范圍限制在所定義的函數(shù)或者文件內(nèi)(取決于此變量定義的位置),而宏定義則沒(méi)有這種特性。
    5. typedef & 復(fù)雜的變量聲明
    在編程實(shí)踐中,尤其是看別人代碼的時(shí)候,常常會(huì)遇到比較復(fù)雜的變量聲明,使用typedef作簡(jiǎn)化自有其價(jià)值,比如:
    下面是三個(gè)變量的聲明,我想使用typdef分別給它們定義一個(gè)別名,請(qǐng)問(wèn)該如何做?
    >1:int *(*a[5])(int, char*);
    >2:void (*b[10]) (void (*)());
    >3. double(*(*pa)[9])();
    答案與分析:
    對(duì)復(fù)雜變量建立一個(gè)類(lèi)型別名的方法很簡(jiǎn)單,你只要在傳統(tǒng)的變量聲明表達(dá)式里用類(lèi)型名替代變量名,然后把關(guān)鍵字typedef加在該語(yǔ)句的開(kāi)頭就行了。
    >1:int *(*a[5])(int, char*);
    //pFun是我們建的一個(gè)類(lèi)型別名
    typedef int *(*pFun)(int, char*);
    //使用定義的新類(lèi)型來(lái)聲明對(duì)象,等價(jià)于int* (*a[5])(int, char*);
    pFun a[5];
    >2:void (*b[10]) (void (*)());
    //首先為上面表達(dá)式藍(lán)色部分聲明一個(gè)新類(lèi)型
    typedef void (*pFunParam)();
    //整體聲明一個(gè)新類(lèi)型
    typedef void (*pFun)(pFunParam);
    //使用定義的新類(lèi)型來(lái)聲明對(duì)象,等價(jià)于void (*b[10]) (void (*)());
    pFun b[10];
    >3. double(*(*pa)[9])();
    //首先為整體聲明一個(gè)新類(lèi)型
    typedef double(*pFun)();
    //再為上面表達(dá)式藍(lán)色部分聲明一個(gè)新類(lèi)型
    typedef pFun (*pFunParam)[9];
    //使用定義的新類(lèi)型來(lái)聲明對(duì)象,等價(jià)于double(*(*pa)[9])();
    pFunParam pa;
    編輯本段
    代碼簡(jiǎn)化

    上面討論的 typedef 行為有點(diǎn)像 #define 宏,用其實(shí)際類(lèi)型替代同義字。不同點(diǎn)是 typedef 在編譯時(shí)被解釋?zhuān)虼俗尵幾g器來(lái)應(yīng)付超越預(yù)處理器能力的文本替換。例如:
    typedef int (*PF) (const char *, const char *);
    這個(gè)聲明引入了 PF 類(lèi)型作為函數(shù)指針的同義字,該函數(shù)有兩個(gè) const char * 類(lèi)型的參數(shù)以及一個(gè) int 類(lèi)型的返回值。如果要使用下列形式的函數(shù)聲明,那么上述這個(gè) typedef 是不可或缺的:
    PF Register(PF pf);
    Register() 的參數(shù)是一個(gè) PF 類(lèi)型的回調(diào)函數(shù),返回某個(gè)函數(shù)的地址,其署名與先前注冊(cè)的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我們是如何實(shí)現(xiàn)這個(gè)聲明的:
    int (*Register (int (*pf)(const char *, const char *)))
    (const char *, const char *);
    很少有程序員理解它是什么意思,更不用說(shuō)這種費(fèi)解的代碼所帶來(lái)的出錯(cuò)風(fēng)險(xiǎn)了。顯然,這里使用 typedef 不是一種特權(quán),而是一種必需。持懷疑態(tài)度的人可能會(huì)問(wèn):"OK,有人還會(huì)寫(xiě)這樣的代碼嗎?",快速瀏覽一下揭示 signal()函數(shù)的頭文件 ,一個(gè)有同樣接口的函數(shù)。
    typedef 和存儲(chǔ)類(lèi)關(guān)鍵字(storage class specifier)
    這種說(shuō)法是不是有點(diǎn)令人驚訝,typedef 就像 auto,extern,mutable,static,和 register 一樣,是一個(gè)存儲(chǔ)類(lèi)關(guān)鍵字。這并不是說(shuō) typedef 會(huì)真正影響對(duì)象的存儲(chǔ)特性;它只是說(shuō)在語(yǔ)句構(gòu)成上,typedef 聲明看起來(lái)象 static,extern 等類(lèi)型的變量聲明。下面將帶到第二個(gè)陷阱:
    typedef register int FAST_COUNTER; // 錯(cuò)誤
    編譯通不過(guò)。問(wèn)題出在你不能在聲明中有多個(gè)存儲(chǔ)類(lèi)關(guān)鍵字。因?yàn)榉?hào) typedef 已經(jīng)占據(jù)了存儲(chǔ)類(lèi)關(guān)鍵字的位置,在 typedef 聲明中不能用 register(或任何其它存儲(chǔ)類(lèi)關(guān)鍵字)。
    編輯本段
    平臺(tái)開(kāi)發(fā)

    typedef 有另外一個(gè)重要的用途,那就是定義機(jī)器無(wú)關(guān)的類(lèi)型,例如,你可以定義一個(gè)叫 REAL 的浮點(diǎn)類(lèi)型,在目標(biāo)機(jī)器上它可以獲得最高的精度:
    typedef long double REAL;
    在不支持 long double 的機(jī)器上,該 typedef 看起來(lái)會(huì)是下面這樣:
    typedef double REAL;
    并且,在連 double 都不支持的機(jī)器上,該 typedef 看起來(lái)會(huì)是這樣:、
    typedef float REAL;
    你不用對(duì)源代碼做任何修改,便可以在每一種平臺(tái)上編譯這個(gè)使用 REAL 類(lèi)型的應(yīng)用程序。唯一要改的是 typedef 本身。在大多數(shù)情況下,甚至這個(gè)微小的變動(dòng)完全都可以通過(guò)奇妙的條件編譯來(lái)自動(dòng)實(shí)現(xiàn)。不是嗎? 標(biāo)準(zhǔn)庫(kù)廣泛地使用 typedef 來(lái)創(chuàng)建這樣的平臺(tái)無(wú)關(guān)類(lèi)型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 這樣的 typedef 還隱藏了長(zhǎng)長(zhǎng)的,難以理解的模板特化語(yǔ)法,例如:basic_string,allocator> 和 basic_ofstream>。
    參考資料
    1.  Bjarne Stroustrup.C++程序設(shè)計(jì)語(yǔ)言.中國(guó):機(jī)械工業(yè)出版社,2010:76-77.
    2.  const char*, char const*, char*const的區(qū)別  .網(wǎng)易博客[引用日期2013-01-20].

     

     

     

    const char*, char const*, char*const的區(qū)別  

    2009-06-05 00:28:16|  分類(lèi): VC |字號(hào) 訂閱
     

    const char*, char const*, char*const的區(qū)別問(wèn)題幾乎是C++面試中每次都會(huì)有的題目。 這個(gè)知識(shí)易混點(diǎn)之前是看過(guò)了,今天做Linux上寫(xiě)GTK程序時(shí)又出現(xiàn)個(gè)Warning,發(fā)散一下又想到這個(gè)問(wèn)題,于是翻起來(lái)重嚼一下。

    事實(shí)上這個(gè)概念誰(shuí)都有只是三種聲明方式非常相似:

    Bjarne在他的The C++ Programming Language里面給出過(guò)一個(gè)助記的方法:

    把一個(gè)聲明從右向左讀。

    char * const cp; ( * 讀成 pointer to ) cp is a const pointer to char

    const char * p; p is a pointer to const char;

    char const * p; 同上因?yàn)镃++里面沒(méi)有const*的運(yùn)算符,所以const只能屬于前面的類(lèi)型。

    C++標(biāo)準(zhǔn)規(guī)定,const關(guān)鍵字放在類(lèi)型或變量名之前等價(jià)的。

    const int n=5; //same as below

    int const m=10

    結(jié)論:

    char * const cp     : 定義一個(gè)指向字符的指針常數(shù),即const指針

    const char* p       : 定義一個(gè)指向字符常數(shù)的指針

    char const* p       : 等同于const char* p

     

    const   char   **是一個(gè)指向指針的指針,那個(gè)指針又指向一個(gè)字符串常量。  
           char   **也是一個(gè)指向指針的指針,那個(gè)指針又指向一個(gè)字符串變量。

    3人 |  分享到:        閱讀(5060)|

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    void指針 指針有兩個(gè)屬性:指向變量/對(duì)象的地址和長(zhǎng)度
    但是指針只存儲(chǔ)地址,長(zhǎng)度則取決于指針的類(lèi)型
    編譯器根據(jù)指針的類(lèi)型從指針指向的地址向后尋址
    指針類(lèi)型不同則尋址范圍也不同,比如:
    int*從指定地址向后尋找4字節(jié)作為變量的存儲(chǔ)單元
    double*從指定地址向后尋找8字節(jié)作為變量的存儲(chǔ)單元

    1.void指針是一種特別的指針
       void *vp
      //說(shuō)它特別是因?yàn)樗鼪](méi)有類(lèi)型
      //或者說(shuō)這個(gè)類(lèi)型不能判斷出指向?qū)ο蟮拈L(zhǎng)度

    2.任何指針都可以賦值給void指針
      type *p;
      vp=p;
      //不需轉(zhuǎn)換
      //只獲得變量/對(duì)象地址而不獲得大小

    3.void指針賦值給其他類(lèi)型的指針時(shí)都要進(jìn)行轉(zhuǎn)換
       type *p=(type*)vp;
       //轉(zhuǎn)換類(lèi)型也就是獲得指向變量/對(duì)象大小
    轉(zhuǎn):http://icoding.spaces.live.com/blog/cns!209684E38D520BA6!130.entry

    4.void指針不能復(fù)引用
      *vp//錯(cuò)誤
      因?yàn)関oid指針只知道,指向變量/對(duì)象的起始地址
      而不知道指向變量/對(duì)象的大小(占幾個(gè)字節(jié))所以無(wú)法正確引用

    5.void指針不能參與指針運(yùn)算,除非進(jìn)行轉(zhuǎn)換
       (type*)vp++;
      //vp==vp+sizeof(type)

     

    #include<iostream>
    #include<stdlib.h>
    #include<string>
    using namespace std;
    typedef struct tag_st
    {
    char id[10];
    float fa[2];
    }ST;
    //我在程序里面這樣使用的
    int main()
    {
    ST * P=(ST *)malloc(sizeof(ST));
    strcpy(P->id,"hello!");
    P->fa[0]=1.1;
    P->fa[1]=2.1;

    ST * Q=(ST *)malloc(sizeof(ST));
    strcpy(Q->id,"world!");
    Q->fa[0]=3.1;
    Q->fa[1]=4.1;
    void ** plink=(void **)P;
    *((ST *)(plink)) = * Q; //plink要先強(qiáng)制轉(zhuǎn)換一下,目的是為了讓它先知道要覆蓋的大小.
                             //P的內(nèi)容竟然給Q的內(nèi)容覆蓋掉了.
    cout<<P->id<<" "<<P->fa[0]<<" "<<P->fa[1]<<endl;
    return 0;
    }
    posted on 2008-09-02 20:17 Dragon 閱讀(10271) 評(píng)論(7)  編輯 收藏 引用 所屬分類(lèi): C++
     
    評(píng)論:
    # re: void指針  Joey Posted @ 2009-07-12 08:17
    寫(xiě)得不錯(cuò),總結(jié)得也還好,  回復(fù)  更多評(píng)論  


    # re: void指針  路過(guò)的 Posted @ 2009-09-17 17:07
    為什么是 void ** plink=(void **)P;

    void ** plink= P; 不行嗎?不是任何類(lèi)型都可以直接付給void指針嗎?
      回復(fù)  更多評(píng)論  


    # re: void指針  學(xué)習(xí)的 Posted @ 2009-09-17 17:16
    void ** plink=(void **)P;
    為什么要二級(jí)指針?
    void * plink=(void *)P; 不行嗎?  回復(fù)  更多評(píng)論  


    # re: void指針  學(xué)習(xí)的 Posted @ 2009-09-17 17:18
    如果要用二級(jí)指針的話(huà),應(yīng)該是
    void ** plink=&p ;  回復(fù)  更多評(píng)論  


    # re: void指針  路過(guò) Posted @ 2009-10-21 11:15
    為什么要用二級(jí)指針啊?void * plink=(void *)P; 也是行的?。?#160; 回復(fù)  更多評(píng)論  


    # re: void指針  yulai_li Posted @ 2009-11-10 15:59
    void * plink=P; 就已經(jīng)可以了  回復(fù)  更多評(píng)論  


    # re: void指針  aa Posted @ 2010-03-02 14:26
    @yulai_li
    不需要二級(jí)指針的。  回復(fù)  更多評(píng)論  

     

     

     

     

    C++ void指針和NULL指針
    博客分類(lèi): C/C++
    CC++C#F#
    我們將從指針的語(yǔ)法和使用并結(jié)合例子來(lái)討論他們的區(qū)別。

     

    Void 指針:


     

    Cpp代碼 
    void * pointer_variable; 

    void * pointer_variable; void這是是作為一個(gè)關(guān)鍵字來(lái)使用。

    參考指針的定義和使用,我們知道所定義指針的數(shù)據(jù)類(lèi)型同指針?biāo)傅臄?shù)據(jù)類(lèi)型是一致的。所分配給指針的地址也必須跟指針類(lèi)型一樣。

    例如:

    Cpp代碼 
    int i;  
    float f;  
    int* exf;  
    float* test;  
    then  
    exf=&i;  

    int i;
    float f;
    int* exf;
    float* test;
    then
    exf=&i;  
    int類(lèi)型指針指向int變量的地址空間,所以是對(duì)的。

    如果寫(xiě)成:

    Cpp代碼 
    exf=&f;  

    exf=&f;  這條語(yǔ)句就會(huì)產(chǎn)生錯(cuò)誤。因?yàn)閕nt類(lèi)型的指針指向的是一塊float變量的地址空間。同樣,如果我們?cè)噲D把float類(lèi)型的指針指向一塊int類(lèi)型的地址空間,也是錯(cuò)誤的,例如:

    Cpp代碼 
    test=&i;  

    test=&i;  上面一條語(yǔ)句將會(huì)報(bào)錯(cuò)。

     

    void類(lèi)型指針是可以用來(lái)指向任何數(shù)據(jù)類(lèi)型的特殊指針。

    使用前面的例子,如果我們手動(dòng)聲明一個(gè)void類(lèi)型指針:

    Cpp代碼 
    void* sample;  

    void* sample;  在前面的例子中,如果我們定義的一個(gè)void類(lèi)型指針去指向一個(gè)float變量的地址空間是完全正確的。

    Cpp代碼 
    sample=&f;  

    sample=&f; 同樣,如果我們把這個(gè)void類(lèi)型指針去指向一個(gè)int類(lèi)型的地址空間也是正確的:

    Cpp代碼 
    sample=&i;  

    sample=&i; void(類(lèi)型)指針,是一種特殊的指針,它足夠靈巧的指向任何數(shù)據(jù)類(lèi)型的地址空間。當(dāng)然它也具有一定的局限:

    在我們要取得指針?biāo)傅刂房臻g的數(shù)據(jù)的時(shí)候使用的是 ‘*’操作符,程序員必須清楚了解到對(duì)于void指針不能使用這種方式來(lái)取得指針?biāo)傅膬?nèi)容。因?yàn)橹苯尤?nèi)容是不允許的。而必須把void指針轉(zhuǎn)換成其他任何valid數(shù)據(jù)類(lèi)型的指針,比如char,int,float等類(lèi)型的指針,之后才能使用'*'取出指針的內(nèi)容。這就是所謂的類(lèi)型轉(zhuǎn)換的概念。

     

    NULL pointer(空指針):

     

    NULL指針的概念不同于前面所說(shuō)的void指針。NULL指針是任何數(shù)據(jù)類(lèi)型指針的一種,并且使用0作為初始值(譯者:這個(gè)好像要跟操作系統(tǒng)有關(guān),有的系統(tǒng)把NULL 指針指向0地址,其他可能指向非零地址,對(duì)吧?有異議請(qǐng)留言)。當(dāng)然這個(gè)不是強(qiáng)制性的。其表明NULL指針并未指向任何一塊合法的(valid)的地址空間。

    舉例:

    Cpp代碼 
    int* exforsys;  
    exforsys=0;  

    int* exforsys;
    exforsys=0;  以上的聲明表示exforsys是一個(gè)int類(lèi)型的指針,但其不指向任何有效的地址空間,這表明exforsys有一個(gè)空指針值(0)。

     

    Void指針和NULL指針的區(qū)別:

    Void指針是一個(gè)可以指向任何數(shù)據(jù)類(lèi)型的特殊指針。NULL指針可是是任何數(shù)據(jù)類(lèi)型的但其不指向任何有效的地址空間或者引用。區(qū)分空指針和指針未被初始化是很關(guān)鍵的,比如,假如程序員寫(xiě)下:

    Cpp代碼 
    #include <iostream.h>  
    int *exforsys;  
    void main()  
    {  
      *exforsys=100;  
    }  

    #include <iostream.h>
    int *exforsys;
    void main()
    {
      *exforsys=100;
    }  上面程序代碼的輸出如下:

     

    NULL POINTER ASSIGNMENT

    上面的程序拋出運(yùn)行時(shí)的錯(cuò)誤。表明指針變量exforsys還沒(méi)有被分配任何有效的地址空間,并且試圖存取0地址空間就產(chǎn)生了錯(cuò)誤信息。

     

    Author: UNKNOWN

     

     

     

     

     

    C/C++語(yǔ)言void及void指針深層探索2006-08-05 06:00 來(lái)源:blog 作者:蔣濤 責(zé)任編輯:方舟·yesky 評(píng)論(16)
    1.概述

    許多初學(xué)者對(duì)C/C++語(yǔ)言中的void及void指針類(lèi)型不甚理解,因此在使用上出現(xiàn)了一些錯(cuò)誤。本文將對(duì)void關(guān)鍵字的深刻含義進(jìn)行解說(shuō),并詳述void及void指針類(lèi)型的使用方法與技巧。

    2.void的含義

    void的字面意思是“無(wú)類(lèi)型”,void *則為“無(wú)類(lèi)型指針”,void *可以指向任何類(lèi)型的數(shù)據(jù)。

    void幾乎只有“注釋”和限制程序的作用,因?yàn)閺膩?lái)沒(méi)有人會(huì)定義一個(gè)void變量,讓我們?cè)囍鴣?lái)定義:

    void a;

    這行語(yǔ)句編譯時(shí)會(huì)出錯(cuò),提示“illegal use of type 'void'”。不過(guò),即使void a的編譯不會(huì)出錯(cuò),它也沒(méi)有任何實(shí)際意義。

    void真正發(fā)揮的作用在于:

    (1)對(duì)函數(shù)返回的限定;

    (2)對(duì)函數(shù)參數(shù)的限定。

    我們將在第三節(jié)對(duì)以上二點(diǎn)進(jìn)行具體說(shuō)明。

    眾所周知,如果指針p1和p2的類(lèi)型相同,那么我們可以直接在p1和p2間互相賦值;如果p1和p2指向不同的數(shù)據(jù)類(lèi)型,則必須使用強(qiáng)制類(lèi)型轉(zhuǎn)換運(yùn)算符把賦值運(yùn)算符右邊的指針類(lèi)型轉(zhuǎn)換為左邊指針的類(lèi)型。

    例如:

    float *p1;
    int *p2;
    p1 = p2;

    其中p1 = p2語(yǔ)句會(huì)編譯出錯(cuò),提示“'=' : cannot convert from 'int *' to 'float *'”,必須改為:

    p1 = (float *)p2;

    而void *則不同,任何類(lèi)型的指針都可以直接賦值給它,無(wú)需進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換:

    void *p1;
    int *p2;
    p1 = p2;

    但這并不意味著,void *也可以無(wú)需強(qiáng)制類(lèi)型轉(zhuǎn)換地賦給其它類(lèi)型的指針。因?yàn)椤盁o(wú)類(lèi)型”可以包容“有類(lèi)型”,而“有類(lèi)型”則不能包容“無(wú)類(lèi)型”。道理很簡(jiǎn)單,我們可以說(shuō)“男人和女人都是人”,但不能說(shuō)“人是男人”或者“人是女人”。下面的語(yǔ)句編譯出錯(cuò):

    void *p1;
    int *p2;
    p2 = p1;

    提示“'=' : cannot convert from 'void *' to 'int *'”。

    3.void的使用

    下面給出void關(guān)鍵字的使用規(guī)則:

    規(guī)則一如果函數(shù)沒(méi)有返回值,那么應(yīng)聲明為void類(lèi)型

    在C語(yǔ)言中,凡不加返回值類(lèi)型限定的函數(shù),就會(huì)被編譯器作為返回整型值處理。但是許多程序員卻誤以為其為void類(lèi)型。例如:

    add ( int a, int b )
    {
    return a + b;
    }
    int main(int argc, char* argv[])
    {
    printf ( "2 + 3 = %d", add ( 2, 3) );
    }

    程序運(yùn)行的結(jié)果為輸出:

    2 + 3 = 5

    這說(shuō)明不加返回值說(shuō)明的函數(shù)的確為int函數(shù)。

    林銳博士《高質(zhì)量C/C++編程》中提到:“C++語(yǔ)言有很?chē)?yán)格的類(lèi)型安全檢查,不允許上述情況(指函數(shù)不加類(lèi)型聲明)發(fā)生”??墒蔷幾g器并不一定這么認(rèn)定,譬如在Visual C++6.0中上述add函數(shù)的編譯無(wú)錯(cuò)也無(wú)警告且運(yùn)行正確,所以不能寄希望于編譯器會(huì)做嚴(yán)格的類(lèi)型檢查。

    因此,為了避免混亂,我們?cè)诰帉?xiě)C/C++程序時(shí),對(duì)于任何函數(shù)都必須一個(gè)不漏地指定其類(lèi)型。如果函數(shù)沒(méi)有返回值,一定要聲明為void類(lèi)型。這既是程序良好可讀性的需要,也是編程規(guī)范性的要求。另外,加上void類(lèi)型聲明后,也可以發(fā)揮代碼的“自注釋”作用。代碼的“自注釋”即代碼能自己注釋自己。

    規(guī)則二如果函數(shù)無(wú)參數(shù),那么應(yīng)聲明其參數(shù)為void

    在C++語(yǔ)言中聲明一個(gè)這樣的函數(shù):

    int function(void)
    {
    return 1;
    }

    則進(jìn)行下面的調(diào)用是不合法的:

    function(2);

    因?yàn)樵贑++中,函數(shù)參數(shù)為void的意思是這個(gè)函數(shù)不接受任何參數(shù)。

    我們?cè)赥urbo C 2.0中編譯:

    #include "stdio.h"
    fun()
    {
    return 1;
    }
    main()
    {
    printf("%d",fun(2));
    getchar();
    }

    編譯正確且輸出1,這說(shuō)明,在C語(yǔ)言中,可以給無(wú)參數(shù)的函數(shù)傳送任意類(lèi)型的參數(shù),但是在C++編譯器中編譯同樣的代碼則會(huì)出錯(cuò)。在C++中,不能向無(wú)參數(shù)的函數(shù)傳送任何參數(shù),出錯(cuò)提示“'fun' : function does not take 1 parameters”。

    所以,無(wú)論在C還是C++中,若函數(shù)不接受任何參數(shù),一定要指明參數(shù)為void。

    規(guī)則三小心使用void指針類(lèi)型

    按照ANSI(American National Standards Institute)標(biāo)準(zhǔn),不能對(duì)void指針進(jìn)行算法操作,即下列操作都是不合法的:

    void * pvoid;
    pvoid++; //ANSI:錯(cuò)誤
    pvoid += 1; //ANSI:錯(cuò)誤
    //ANSI標(biāo)準(zhǔn)之所以這樣認(rèn)定,是因?yàn)樗鼒?jiān)持:進(jìn)行算法操作的指針必須是確定知道其指向數(shù)據(jù)類(lèi)型大小的。
    //例如:
    int *pint;
    pint++; //ANSI:正確

    pint++的結(jié)果是使其增大sizeof(int)。

    但是大名鼎鼎的GNU(GNU's Not Unix的縮寫(xiě))則不這么認(rèn)定,它指定void *的算法操作與char *一致。

    因此下列語(yǔ)句在GNU編譯器中皆正確:

    pvoid++; //GNU:正確
    pvoid += 1; //GNU:正確

    pvoid++的執(zhí)行結(jié)果是其增大了1。

    在實(shí)際的程序設(shè)計(jì)中,為迎合ANSI標(biāo)準(zhǔn),并提高程序的可移植性,我們可以這樣編寫(xiě)實(shí)現(xiàn)同樣功能的代碼:

    void * pvoid;
    (char *)pvoid++; //ANSI:正確;GNU:正確
    (char *)pvoid += 1; //ANSI:錯(cuò)誤;GNU:正確

    GNU和ANSI還有一些區(qū)別,總體而言,GNU較ANSI更“開(kāi)放”,提供了對(duì)更多語(yǔ)法的支持。但是我們?cè)谡鎸?shí)設(shè)計(jì)時(shí),還是應(yīng)該盡可能地迎合ANSI標(biāo)準(zhǔn)。

    規(guī)則四如果函數(shù)的參數(shù)可以是任意類(lèi)型指針,那么應(yīng)聲明其參數(shù)為void *

    典型的如內(nèi)存操作函數(shù)memcpy和memset的函數(shù)原型分別為:

    void * memcpy(void *dest, const void *src, size_t len);
    void * memset ( void * buffer, int c, size_t num );

    這樣,任何類(lèi)型的指針都可以傳入memcpy和memset中,這也真實(shí)地體現(xiàn)了內(nèi)存操作函數(shù)的意義,因?yàn)樗僮鞯膶?duì)象僅僅是一片內(nèi)存,而不論這片內(nèi)存是什么類(lèi)型。如果memcpy和memset的參數(shù)類(lèi)型不是void *,而是char *,那才叫真的奇怪了!這樣的memcpy和memset明顯不是一個(gè)“純粹的,脫離低級(jí)趣味的”函數(shù)!

    下面的代碼執(zhí)行正確:

    //示例:memset接受任意類(lèi)型指針
    int intarray[100];
    memset ( intarray, 0, 100*sizeof(int) ); //將intarray清0
    //示例:memcpy接受任意類(lèi)型指針
    int intarray1[100], intarray2[100];
    memcpy ( intarray1, intarray2, 100*sizeof(int) ); //將intarray2拷貝給intarray1

    有趣的是,memcpy和memset函數(shù)返回的也是void *類(lèi)型,標(biāo)準(zhǔn)庫(kù)函數(shù)的編寫(xiě)者是多么地富有學(xué)問(wèn)啊!

    規(guī)則五 void不能代表一個(gè)真實(shí)的變量

    下面代碼都企圖讓void代表一個(gè)真實(shí)的變量,因此都是錯(cuò)誤的代碼:

    void a; //錯(cuò)誤
    function(void a); //錯(cuò)誤

    void體現(xiàn)了一種抽象,這個(gè)世界上的變量都是“有類(lèi)型”的,譬如一個(gè)人不是男人就是女人(還有人妖?)。

    void的出現(xiàn)只是為了一種抽象的需要,如果你正確地理解了面向?qū)ο笾小俺橄蠡?lèi)”的概念,也很容易理解void數(shù)據(jù)類(lèi)型。正如不能給抽象基類(lèi)定義一個(gè)實(shí)例,我們也不能定義一個(gè)void(讓我們類(lèi)比的稱(chēng)void為“抽象數(shù)據(jù)類(lèi)型”)變量。

    4.總結(jié)

    小小的void蘊(yùn)藏著很豐富的設(shè)計(jì)哲學(xué),作為一名程序設(shè)計(jì)人員,對(duì)問(wèn)題進(jìn)行深一個(gè)層次的思考必然使我們受益匪淺

     

     

     


    void類(lèi)型以及void指針的使用方法更新時(shí)間:2011-04-27 13:18:57  來(lái)源:愛(ài)程序網(wǎng)整理  點(diǎn)擊:5253-
    -
    一、void的含義

    void的字面意思是“無(wú)類(lèi)型”,void *則為“無(wú)類(lèi)型指針”,void *可以指向任何類(lèi)型的數(shù)據(jù)。

    void幾乎只有“注釋”和限制程序的作用,因?yàn)閺膩?lái)沒(méi)有人會(huì)定義一個(gè)void變量,讓我們?cè)囍鴣?lái)定義:


    void a;

    這行語(yǔ)句編譯時(shí)會(huì)出錯(cuò),提示“illegal use of type 'void'”。不過(guò),即使void a的編譯不會(huì)出錯(cuò),它也沒(méi)有任何實(shí)際意義。

    void真正發(fā)揮的作用在于:
    (1) 對(duì)函數(shù)返回的限定;
    (2) 對(duì)函數(shù)參數(shù)的限定。

    我們將在第三節(jié)對(duì)以上二點(diǎn)進(jìn)行具體說(shuō)明。

    眾所周知,如果指針p1和p2的類(lèi)型相同,那么我們可以直接在p1和p2間互相賦值;如果p1和p2指向不同的數(shù)據(jù)類(lèi)型,則必須使用強(qiáng)制類(lèi)型

    轉(zhuǎn)換運(yùn)算符把賦值運(yùn)算符右邊的指針類(lèi)型轉(zhuǎn)換為左邊指針的類(lèi)型。

    例如:
    float *p1;
    int *p2;
    p1 = p2;

    其中p1 = p2語(yǔ)句會(huì)編譯出錯(cuò),提示“'=' : cannot convert from 'int *' to 'float *'”,必須改為:
    p1 = (float *)p2;
    而void *則不同,任何類(lèi)型的指針都可以直接賦值給它,無(wú)需進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換:
    void *p1;
    int *p2;
    p1 = p2;

    但這并不意味著,void *也可以無(wú)需強(qiáng)制類(lèi)型轉(zhuǎn)換地賦給其它類(lèi)型的指針。因?yàn)椤盁o(wú)類(lèi)型”可以包容“有類(lèi)型”,而“有類(lèi)型”則不能包

    容“無(wú)類(lèi)型”。道理很簡(jiǎn)單,我們可以說(shuō)“男人和女人都是人”,但不能說(shuō)“人是男人”或者“人是女人”。下面的語(yǔ)句編譯出錯(cuò):
    void *p1;
    int *p2;
    p2 = p1;

    提示“'=' : cannot convert from 'void *' to 'int *'”。

    二、void指針使用實(shí)例


    int download_addr;
    void abc(void)
    {
    download_addr = 0x0c400000;
    void (*fun)(void);
    fun = (void (*)(void))download_addr;
    (*fun)();
    }

    注釋?zhuān)?
    void (*fun)(void); 定義一個(gè)函數(shù)指針func 輸入?yún)?shù)為void返回類(lèi)型為void
    fun = (void (*)(void))download_addr;
    這句話(huà)是將download_addr這個(gè)函數(shù)指針強(qiáng)制轉(zhuǎn)換為參數(shù)為void返回類(lèi)型為void的函數(shù)指針,然后賦值給func
    最后一句就是要執(zhí)行這個(gè)函數(shù) 
    download_addr 是一個(gè)函數(shù)指針
    來(lái)源:豆芽博客,地址:http://www.aichengxu.com/article/c%E8%AF%AD%E8%A8%80/149_11.html保留原文鏈接,是開(kāi)源分享的開(kāi)始.

     

     

     

     

     

     

     

     

    C 語(yǔ)言中的泛型
    2012 年 11 月 15 日 孫鶴 C, 2

    很多高級(jí)語(yǔ)言有很強(qiáng)的泛型編程機(jī)制,例如: C++ 的模板;python 中的函數(shù)并不需要定義參數(shù)類(lèi)型;這些都是泛型編程的機(jī)制。

    但是 C 程序員依然渴望自己寫(xiě)的一些東西可以得到復(fù)用,例如實(shí)現(xiàn)了一個(gè)鏈表,總是希望它可以應(yīng)用在很多場(chǎng)景下。

    那么 C 語(yǔ)言如何實(shí)現(xiàn)泛型編程呢?在Linux 下 C 語(yǔ)言大概提供了兩種機(jī)制實(shí)現(xiàn)泛型編程。

    void * 指針

    C 語(yǔ)言是操作內(nèi)存最容易的語(yǔ)言,而 void * 指針代表的是指向任意的內(nèi)容,所以泛型用它再適合不過(guò)了。但是 void * 指向的內(nèi)容在使用的時(shí)候,必須顯示的轉(zhuǎn)換成你想要的類(lèi)型,或者知道它的大小。

    typeof 方式

    這種方式并不是所有的編譯器都支持,但是在 Linux 下的各種編譯器完全沒(méi)問(wèn)題。通常這種方式都需要跟宏配合使用,但是可以達(dá)到你意想不到的效果。


    --------------------------------------------------------------------------------

    下面來(lái)看個(gè)實(shí)際的簡(jiǎn)單例子吧,例如我希望實(shí)現(xiàn)一個(gè)兩個(gè)數(shù)互換的方法。分別用 void * 和 typof 方式實(shí)現(xiàn):

    1234567891011121314151617181920212223242526272829303132333435363738 #include <stdio.h> #include <stdlib.h> #include <string.h>   #define typeof_swap(v1, v2) \     do {                    \         typeof(v1) tmp;     \         tmp = v1;           \         v1 = v2;            \         v2 = tmp;           \     }                       \     while(0)    voidvoidptr_swap(void *p1, void *p2, size_t size) {     void    *tmp;       tmp = malloc(size);     memcpy(tmp, p1, size);     memcpy(p1, p2, size);     memcpy(p2, tmp, size);     free(tmp); }   intmain(int argc, char *argv[]) {     int a = 2;     int b = 3;       voidptr_swap(&a, &b, sizeof(int));     printf("voidptr_swap:\na: %d\nb: %d\n", a, b);      typeof_swap(a, b);      printf("typeof_swap:\na: %d\nb: %d\n", a, b);        return 0; }


    線(xiàn)性結(jié)構(gòu)的思考 vim 中 taglist 插件的使用
    2 thoughts on “C 語(yǔ)言中的泛型”
    coder_L 說(shuō)道:
    2013 年 5 月 14 日 21:21
    typeof 是C99的關(guān)鍵字? 我用gcc -std=c89 main.c和gcc -std=c99 main.c 都出現(xiàn)tmp未聲明的錯(cuò)誤信息。但是默認(rèn)gcc main.c就沒(méi)錯(cuò)誤而且運(yùn)行正確, 這是什么原因? 我印象中似乎c99沒(méi)增加這個(gè)關(guān)鍵字啊。額, 還有memcpy移動(dòng)內(nèi)存之前, 用不用檢查內(nèi)存重疊?

    回復(fù)  孫鶴 說(shuō)道:
    2013 年 5 月 24 日 09:05
    typeof 屬于 gcc 都支持的,但是 windows 編譯器不支持,具體我不清楚是不是 C99 才支持的。memcpy 我記得是不檢查的,具體可以查下 glibc 代碼。

    posted on 2013-09-10 14:14 小高 閱讀(534) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): C

    導(dǎo)航

    <2013年9月>
    25262728293031
    1234567
    891011121314
    15161718192021
    22232425262728
    293012345

    統(tǒng)計(jì)

    常用鏈接

    留言簿(3)

    隨筆分類(lèi)(352)

    收藏夾(19)

    關(guān)注的blog

    手冊(cè)

    搜索

    積分與排名

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    主站蜘蛛池模板: 国产成人精品免费视| 亚洲六月丁香六月婷婷蜜芽| 国产精品成人69XXX免费视频| 日韩精品成人亚洲专区| MM1313亚洲精品无码久久| 成人免费男女视频网站慢动作| 天堂亚洲国产中文在线| 亚洲人成网站免费播放| 7777久久亚洲中文字幕| 免费电视剧在线观看| 日韩亚洲不卡在线视频中文字幕在线观看| 岛国av无码免费无禁网站| 中文字幕亚洲综合久久综合| 四虎免费大片aⅴ入口| 成人精品国产亚洲欧洲| 国产特级淫片免费看| 深夜A级毛片视频免费| 亚洲人成网站色在线入口| 国产精品视频全国免费观看| 亚洲午夜福利717| 久久久久久一品道精品免费看| 久久久无码精品亚洲日韩京东传媒 | 免费国产黄网站在线看| xvideos亚洲永久网址| a级毛片免费观看在线| 亚洲av中文无码乱人伦在线咪咕 | 国产乱子伦精品免费女| 一区二区三区在线观看免费| 亚洲日本乱码在线观看| 日韩视频在线观看免费| 亚洲成a人片毛片在线| 性感美女视频免费网站午夜| WWW亚洲色大成网络.COM| 亚洲综合激情另类专区| 男的把j放进女人下面视频免费| 亚洲黄色免费电影| 在线观看免费宅男视频| 少妇亚洲免费精品| 亚洲av无码无在线观看红杏| 免费观看无遮挡www的视频| 亚洲av日韩精品久久久久久a|