JUST DO IT ~

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

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

           

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

           

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

           

           

           

           

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

           

           

           


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

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

          下面是錢能教程中的一段代碼:

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

           

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

           

           

           

           

           


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

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

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

              void func1( MYCLASS *&pBuildingElement );

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          MYCLASS* p = NULL;
            func1(p);

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

            至于說什么場合要使用這種方法,我會說,極少。MFC在其集合類中用到了它--例如,CObList,它是一個Cobjects指針列表。

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

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

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

               Cobject* pObj = mylist.GetAt(pos);

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

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

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

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


          歷史上的今天:

           

           

           

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

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


          再說 c99 也沒完全兼容以前的c

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


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

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


          通常不涉及對象時,指針強(qiáng)轉(zhuǎn),沒有副作用

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

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

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

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

           

           

           

           


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

          上一節(jié) 下一節(jié) 返回目錄編輯/糾錯/意見關(guān)注(146)更新:2012-12-31
          分享到0
          在C 語言中,你可以隱式地將*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 開始的內(nèi)存 */
              printf("%d %d\n",i,j);
          }

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

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

          造型被最好地避免了。

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

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

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

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

           

           

           

           

           

           

           

           

           

           

           

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

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

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

          按位與運(yùn)算通常用來對某些位清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ù)各對應(yīng)的二進(jìn)位相或。只要對應(yīng)的二個二進(jìn)位有一個為1時,結(jié)果位就為1。參與運(yùn)算的兩個數(shù)均以補(bǔ)碼出現(xiàn)。
          例如:9|5可寫算式如下: 00001001|00000101
          00001101 (十進(jìn)制為13)可見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ù)各對應(yīng)的二進(jìn)位相異或,當(dāng)兩對應(yīng)的二進(jìn)位相異時,結(jié)果為1。參與運(yùn)算數(shù)仍以補(bǔ)碼出現(xiàn),例如9^5可寫成算式如下: 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é)合性。 其功能是對參與運(yùn)算的數(shù)的各二進(jìn)位按位求反。例如~9的運(yùn)算為: ~(0000000000001001)結(jié)果為:1111111111110110

          5. 左移運(yùn)算 左移運(yùn)算符“<<”是雙目運(yùn)算符。其功能把“<< ”左邊的運(yùn)算數(shù)的各二進(jìn)位全部左移若干位,由“<<”右邊的數(shù)指定移動的位數(shù),
          高位丟棄,低位補(bǔ)0。例如: a<<4 指把a(bǔ)的各二進(jìn)位向左移動4位。如a=00000011(十進(jìn)制3),左移4位后為00110000(十進(jìn)制48)。6. 右移運(yùn)算 右移運(yùn)算符“>>”是雙目運(yùn)算符。其功能是把“>> ”左邊的運(yùn)算數(shù)的各二進(jìn)位全部右移若干位,“>>”右邊的數(shù)指定移動的位數(shù)。
          例如:設(shè) a=15,a>>2 表示把000001111右移為00000011(十進(jìn)制3)。 應(yīng)該說明的是,對于有符號數(shù),在右移時,符號位將隨同移動。當(dāng)為正數(shù)時, 最高位補(bǔ)0,而為負(fù)數(shù)時,符號位為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);
          }
          請?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);
          }

          位域

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

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

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

          3. 位域可以無位域名,這時它只用來作填充或調(diào)整位置。無名的位域是不能使用的。例如:
          struct k
          {
          int a:1
          int :2 /*該2位不能使用*/
          int b:3
          int c:2
          };
          從以上分析可以看出,位域在本質(zhì)上就是一種結(jié)構(gòu)類型, 不過其成員是按二進(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,三個位域?yàn)閍,b,c。說明了bs類型的變量bit和指向bs類型的指針變量pbit。這表示位域也是可以使用指針的。
          程序的9、10、11三行分別給三個位域賦值。( 應(yīng)注意賦值不能超過該位域的允許范圍)程序第12行以整型量格式輸出三個域的內(nèi)容。第13行把位域變量bit的地址送給指針變量pbit。第14行用指針方式給位域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行用指針方式輸出了這三個域的值。

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

          浮點(diǎn)數(shù)的存儲格式是符號+階碼(定點(diǎn)整數(shù))+尾數(shù)(定點(diǎn)小數(shù))
          SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM
          即1位符號位(0為正,1為負(fù)),8位指數(shù)位,23位尾數(shù)位
          浮點(diǎn)數(shù)存儲前先轉(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
          所以,對浮點(diǎn)數(shù)*2、/2只要對8位符號位+、- 即可,但不是左移、右移

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

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

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

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

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

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

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


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

          我們先看如下測試代碼:

          /////////////////////////////////////////////////
          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)行很正確嘛……于是我們就放心的在自己的程序中使用起這個函數(shù)來了。

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

          “郁悶!”你說,“這個函數(shù)怎么會有問題呢!?”

          可是,更郁悶的還在后頭呢,因?yàn)槟惆殉绦蛑械妮斎肓孔鳛閰?shù),在一個簡單的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檔案的真相]:

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

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

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

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

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

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

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

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

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

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

          如果我們在函數(shù)中用

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

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

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

          sLowBits = 7bcd

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

          sLowBits = ffff8bcd

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

          因此,我們看出來了:

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

          0xffff8bcd

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

          0xffff8bcd

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

          0x00007bcd

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

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

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

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

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

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

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

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

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

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

          如果有一天你把一只兔子扔給一只老虎,老虎把兔子吃了,第二天把一只老鼠扔給它,它又吃了,那么說明第一天你看錯了:它本來就是一只貓。


          全文結(jié)束

           

           

           

           

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

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

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

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

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

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

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

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

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

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

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

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

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

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


          對我有用[0] 丟個板磚[0] 引用 | 舉報 | 管理

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

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

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

          Jalien
          Jalien
          等級:
          #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報錯:error C2574: “Test::Test(void)”: 不能聲明為靜態(tài)的
          gcc報錯:   error: constructor cannot be static member function


          對我有用[0] 丟個板磚[0] 引用 | 舉報 | 管理

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

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

          C++做不到的呵……
          對我有用[0] 丟個板磚[0] 引用 | 舉報 | 管理

          hikaliv
          hikaliv
          等級:
          #19 得分:0 回復(fù)于: 2009-06-23 15:56:55
          又說錯話了……
          對我有用[0] 丟個板磚[0] 引用 | 舉報 | 管理

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

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

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

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

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

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

          sszwbuaa
          sszwbuaa
          等級:
          #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報錯:error C2574: “Test::Test(void)”: 不能聲明為靜態(tài)的  gcc報錯:  error: constructor cannot be static member function

          實(shí)踐出真知!
          對我有用[0] 丟個板磚[0] 引用 | 舉報 | 管理

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

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

          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 ();
          對我有用[0] 丟個板磚[0] 引用 | 舉報 | 管理

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

          對我有用[0] 丟個板磚[0] 引用 | 舉報 | 管理

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

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

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

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

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

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


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

          對我有用[0] 丟個板磚[0] 引用 | 舉報 | 管理

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

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

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

          但是這個過程卻可以模擬!!
          對我有用[0] 丟個板磚[0] 引用 | 舉報 | 管理

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


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

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

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

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

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

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

          lthyxy
          那個無知的少年
          等級:
          #39 得分:0 回復(fù)于: 2010-06-02 14:07:04
          編譯器會自動生成
          對我有用[0] 丟個板磚[0] 引用 | 舉報 | 管理

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

          但是C#可以的哈。

           

           

           

           


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

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

          2.void的含義

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

          void幾乎只有“注釋”和限制程序的作用,因?yàn)閺膩頉]有人會定義一個void變量,讓我們試著來定義:

          void a;

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

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

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

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

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

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

          例如:

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

          其中p1 = p2語句會編譯出錯,提示“'=' : cannot convert from 'int *' to 'float *'”,必須改為:

          p1 = (float *)p2;

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

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

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

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

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

          3.void的使用

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

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

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

          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

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

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

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

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

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

          int function(void)
          {
          return 1;
          }

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

          function(2);

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

          我們在Turbo C 2.0中編譯:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          規(guī)則四如果函數(shù)的參數(shù)可以是任意類型指針,那么應(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 );

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

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

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

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

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

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

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

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

          void的出現(xiàn)只是為了一種抽象的需要,如果你正確地理解了面向?qū)ο笾小俺橄蠡悺钡母拍睿埠苋菀桌斫鈜oid數(shù)據(jù)類型。正如不能給抽象基類定義一個實(shí)例,我們也不能定義一個void(讓我們類比的稱void為“抽象數(shù)據(jù)類型”)變量。

          4.總結(jié)

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

           

           

           

           

           

           

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

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

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

           

           


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

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

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

          int * const p

          先看const再看* ,是p是一個常量類型的指針,不能修改這個指針的指向,但是這個指針?biāo)赶虻牡刂飞洗鎯Φ闹悼梢孕薷摹?
          實(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不能再這樣重新賦值了,即不能再指向另一個新地址。所以我已經(jīng)注釋了它。
              printf("%d\n", *pi ) ;   //輸出是30
              i1=80;     //5.想想看:這里能用*pi=80;來代替嗎?可以,這里可以通過*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,定義一個指針指向一個常量,不能通過指針來修改這個指針指向的值。
          實(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可以在任意時候重新賦值一個新內(nèi)存地址
              i2=80;     //想想看:這里能用*pi=80;來代替嗎?當(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ù)是一個常量,其值不能被修改。
          3.指向常量的常量指針
          對于“指向常量的常量指針”,就必須同時滿足上述1和2中的內(nèi)容,既不可以修改指針的值,也不可以修改指針指向的值。
          4.引入字符數(shù)組和字符指針
          字符數(shù)組和字符指針的定義如下:
          復(fù)制代碼 代碼如下:

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

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

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

            *exforsys=100; 
          }  
          上面程序代碼的輸出如下:
           
          NULL POINTER ASSIGNMENT
          上面的程序拋出運(yùn)行時的錯誤。表明指針變量exforsys還沒有被分配任何有效的地址空間,并且試圖存取0地址空間就產(chǎn)生了錯誤信息。
           
          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ù)。沒有作者書面許可不得轉(zhuǎn)載。
          推薦鏈接
          Java開發(fā)新方式:專注UI,快速開發(fā)!
          返回頂樓        
          agurick
          等級: 初級會員

          性別:
          文章: 59
          積分: 10
          來自: 陜西

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

          性別:
          文章: 28
          積分: 30
          來自: 徐州

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

          一針見血

           

           

           

           

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

          上一節(jié) 下一節(jié) 返回目錄編輯/糾錯/意見關(guān)注(146)更新:2012-12-31
          分享到0
          在C 語言中,你可以隱式地將*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 開始的內(nèi)存 */
              printf("%d %d\n",i,j);
          }

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

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

          造型被最好地避免了。

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

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

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

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

           

           

           


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

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

          #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)為對象B分配了內(nèi)存并完成了與對象A的復(fù)制過程。就類對象而言,相同類型的類對象是通過拷貝構(gòu)造函數(shù)來完成整個復(fù)制過程的。下面舉例說明拷貝構(gòu)造函數(shù)的工作過程。

          #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ù)。可見,拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),函數(shù)的名稱必須和類名稱一致,它的唯一的一個參數(shù)是本類型的一個引用變量,該參數(shù)是const類型,不可變的。例如:類X的拷貝構(gòu)造函數(shù)的形式為X(X& x)。

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

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

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

          淺拷貝和深拷貝

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

          深拷貝和淺拷貝可以簡單理解為:如果一個類擁有資源,當(dāng)這個類的對象發(fā)生復(fù)制過程的時候,資源重新分配,這個過程就是深拷貝,反之,沒有重新分配資源,就是淺拷貝。下面舉個深拷貝的例子。

          #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;
          }

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

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

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

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

           

           


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

          一、 C語言中的main() 在C89中,main( )是可以接受的。Brian W. Kernighan和Dennis M. Ritchie的經(jīng)典巨著The C programming Language 2e(《C 程序設(shè)計(jì)語言第二版》)用的就是main( )。不過在最新的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)小小的改動。例如:char *argv[]可以寫成char **argv;argv和argc可以改成別的變量名(如intval和charval),不過一定要符合變量的命名規(guī)則。

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

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

          如果main函數(shù)的最后沒有寫return語句的話,C99規(guī)定編譯器要自動在生成的目標(biāo)文件中(如exe文件)加入return 0;,表示程序正常退出。不過,我還是建議你最好在main函數(shù)的最后加上return語句,雖然沒有這個必要,但這是一個好的習(xí)慣。注意,vc6不會在目標(biāo)文件中加入return 0;,大概是因?yàn)関c6是98年的產(chǎn)品,所以才不支持這個特性。現(xiàn)在明白我為什么建議你最好加上return語句了吧!不過,gcc3.2(Linux下的C編譯器)會在生成的目標(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ù)的返回值類型也必須是int。如果main函數(shù)的末尾沒寫return語句,C++98規(guī)定編譯器要自動在生成的目標(biāo)文件中加入return 0;。同樣,vc6也不支持這個特性,但是g++3.2(Linux下的C++編譯器)支持。
          三、 關(guān)于void main() 在C和C++中,不接收任何參數(shù)也不返回任何信息的函數(shù)原型為“void foo(void);”。可能正是因?yàn)檫@個,所以很多人都誤認(rèn)為如果不需要程序返回值時可以把main函數(shù)定義成void main(void)。然而這是錯誤的!main函數(shù)的返回值應(yīng)該定義為int類型,C和C++標(biāo)準(zhǔn)中都是這樣規(guī)定的。雖然在一些編譯器中,void main可以通過編譯(如vc6),但并非所有編譯器都支持void main,因?yàn)闃?biāo)準(zhǔn)中從來沒有定義過void main。g++3.2中如果main函數(shù)的返回值不是int類型,就根本通不過編譯。而gcc3.2則會發(fā)出警告。所以,如果你想你的程序擁有很好的可移植性,請一定要用int main。

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

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

          int main(void)

          {

          return 0;

          }

          然后打開附件里的“命令提示符”,在命令行里運(yùn)行剛才編譯好的可執(zhí)行文件,然后輸入“echo %ERRORLEVEL%”,回車,就可以看到程序的返回值為0。假設(shè)剛才編譯好的文件是a.exe,如果輸入“a && dir”,則會列出當(dāng)前目錄下的文件夾和文件。但是如果改成“return -1”,或者別的非0值,重新編譯后輸入“a && dir”,則dir不會執(zhí)行。因?yàn)?amp;&的含義是:如果&&前面的程序正常退出,則繼續(xù)執(zhí)行&&后面的程序,否則不執(zhí)行。也就是說,利用程序的返回值,我們可以控制要不要執(zhí)行下一個程序。這就是int main的好處。如果你有興趣,也可以把main函數(shù)的返回值類型改成非int類型(如float),重新編譯后執(zhí)行“a && dir”,看看會出現(xiàn)什么情況,想想為什么會出現(xiàn)那樣的情況。順便提一下,如果輸入a || dir的話,則表示如果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),所以并非所有編譯器都支持,故而移植性差,不推薦使用——除非你的程序是專門設(shè)計(jì)用于工作在特定的環(huán)境中而且需要獲取系統(tǒng)的環(huán)境變量。


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

           

           

           

           


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

          一、 C語言中的main() 在C89中,main( )是可以接受的。Brian W. Kernighan和Dennis M. Ritchie的經(jīng)典巨著The C programming Language 2e(《C 程序設(shè)計(jì)語言第二版》)用的就是main( )。不過在最新的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)小小的改動。例如:char *argv[]可以寫成char **argv;argv和argc可以改成別的變量名(如intval和charval),不過一定要符合變量的命名規(guī)則。

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

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

          如果main函數(shù)的最后沒有寫return語句的話,C99規(guī)定編譯器要自動在生成的目標(biāo)文件中(如exe文件)加入return 0;,表示程序正常退出。不過,我還是建議你最好在main函數(shù)的最后加上return語句,雖然沒有這個必要,但這是一個好的習(xí)慣。注意,vc6不會在目標(biāo)文件中加入return 0;,大概是因?yàn)関c6是98年的產(chǎn)品,所以才不支持這個特性。現(xiàn)在明白我為什么建議你最好加上return語句了吧!不過,gcc3.2(Linux下的C編譯器)會在生成的目標(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ù)的返回值類型也必須是int。如果main函數(shù)的末尾沒寫return語句,C++98規(guī)定編譯器要自動在生成的目標(biāo)文件中加入return 0;。同樣,vc6也不支持這個特性,但是g++3.2(Linux下的C++編譯器)支持。
          三、 關(guān)于void main() 在C和C++中,不接收任何參數(shù)也不返回任何信息的函數(shù)原型為“void foo(void);”。可能正是因?yàn)檫@個,所以很多人都誤認(rèn)為如果不需要程序返回值時可以把main函數(shù)定義成void main(void)。然而這是錯誤的!main函數(shù)的返回值應(yīng)該定義為int類型,C和C++標(biāo)準(zhǔn)中都是這樣規(guī)定的。雖然在一些編譯器中,void main可以通過編譯(如vc6),但并非所有編譯器都支持void main,因?yàn)闃?biāo)準(zhǔn)中從來沒有定義過void main。g++3.2中如果main函數(shù)的返回值不是int類型,就根本通不過編譯。而gcc3.2則會發(fā)出警告。所以,如果你想你的程序擁有很好的可移植性,請一定要用int main。

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

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

          int main(void)

          {

          return 0;

          }

          然后打開附件里的“命令提示符”,在命令行里運(yùn)行剛才編譯好的可執(zhí)行文件,然后輸入“echo %ERRORLEVEL%”,回車,就可以看到程序的返回值為0。假設(shè)剛才編譯好的文件是a.exe,如果輸入“a && dir”,則會列出當(dāng)前目錄下的文件夾和文件。但是如果改成“return -1”,或者別的非0值,重新編譯后輸入“a && dir”,則dir不會執(zhí)行。因?yàn)?amp;&的含義是:如果&&前面的程序正常退出,則繼續(xù)執(zhí)行&&后面的程序,否則不執(zhí)行。也就是說,利用程序的返回值,我們可以控制要不要執(zhí)行下一個程序。這就是int main的好處。如果你有興趣,也可以把main函數(shù)的返回值類型改成非int類型(如float),重新編譯后執(zhí)行“a && dir”,看看會出現(xiàn)什么情況,想想為什么會出現(xiàn)那樣的情況。順便提一下,如果輸入a || dir的話,則表示如果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),所以并非所有編譯器都支持,故而移植性差,不推薦使用——除非你的程序是專門設(shè)計(jì)用于工作在特定的環(huán)境中而且需要獲取系統(tǒng)的環(huán)境變量。


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

           

           

           

           

           

           

           

           

           


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

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

          void幾乎只有“注釋”和限制程序的作用,因?yàn)閺膩頉]有人會定義一個void變量,讓我們試著來定義:

          void a;

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

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

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

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

          例如:

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

          其中p1 = p2語句會編譯出錯,提示“'=' : cannot convert from 'int *' to 'float *'”,必須改為:

          p1 = (float *)p2;

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

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


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

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


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

          3.void的使用

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

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

          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
          這說明不加返回值說明的函數(shù)的確為int函數(shù)。

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

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

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

          int function(void)
          {
          return 1;
          }


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

          function(2);


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

          我們在Turbo C 2.0中編譯:

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


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

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

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

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

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


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

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

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


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

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

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


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

          規(guī)則四 如果函數(shù)的參數(shù)可以是任意類型指針,那么應(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 );


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

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

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

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


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

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

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

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


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

          void的出現(xiàn)只是為了一種抽象的需要,如果你正確地理解了面向?qū)ο笾小俺橄蠡悺钡母拍睿埠苋菀桌斫鈜oid數(shù)據(jù)類型。正如不能給抽象基類定義一個實(shí)例,我們也不能定義一個void(讓我們類比的稱void為“抽象數(shù)據(jù)類型”)變量。

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

           

           

           

           

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

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

          #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)為對象B分配了內(nèi)存并完成了與對象A的復(fù)制過程。就類對象而言,相同類型的類對象是通過拷貝構(gòu)造函數(shù)來完成整個復(fù)制過程的。下面舉例說明拷貝構(gòu)造函數(shù)的工作過程。

          #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ù)。可見,拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),函數(shù)的名稱必須和類名稱一致,它的唯一的一個參數(shù)是本類型的一個引用變量,該參數(shù)是const類型,不可變的。例如:類X的拷貝構(gòu)造函數(shù)的形式為X(X& x)。

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

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

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

          淺拷貝和深拷貝

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

          深拷貝和淺拷貝可以簡單理解為:如果一個類擁有資源,當(dāng)這個類的對象發(fā)生復(fù)制過程的時候,資源重新分配,這個過程就是深拷貝,反之,沒有重新分配資源,就是淺拷貝。下面舉個深拷貝的例子。

          #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;
          }

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

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

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

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

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

           

           

           

           

           

           

           

           

           

           

           

           

           


          typedef

          百科名片

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

          定義
          用法總結(jié)
          語言用法
          代碼簡化
          平臺開發(fā)
          編輯本段
          定義

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

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

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

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

          typedef 有另外一個重要的用途,那就是定義機(jī)器無關(guān)的類型,例如,你可以定義一個叫 REAL 的浮點(diǎn)類型,在目標(biāo)機(jī)器上它可以獲得最高的精度:
          typedef long double REAL;
          在不支持 long double 的機(jī)器上,該 typedef 看起來會是下面這樣:
          typedef double REAL;
          并且,在連 double 都不支持的機(jī)器上,該 typedef 看起來會是這樣:、
          typedef float REAL;
          你不用對源代碼做任何修改,便可以在每一種平臺上編譯這個使用 REAL 類型的應(yīng)用程序。唯一要改的是 typedef 本身。在大多數(shù)情況下,甚至這個微小的變動完全都可以通過奇妙的條件編譯來自動實(shí)現(xiàn)。不是嗎? 標(biāo)準(zhǔn)庫廣泛地使用 typedef 來創(chuàng)建這樣的平臺無關(guān)類型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 這樣的 typedef 還隱藏了長長的,難以理解的模板特化語法,例如:basic_string,allocator> 和 basic_ofstream>。
          參考資料
          1.  Bjarne Stroustrup.C++程序設(shè)計(jì)語言.中國:機(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|  分類: VC |字號 訂閱
           

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

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

          Bjarne在他的The C++ Programming Language里面給出過一個助記的方法:

          把一個聲明從右向左讀。

          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)镃++里面沒有const*的運(yùn)算符,所以const只能屬于前面的類型。

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

          const int n=5; //same as below

          int const m=10

          結(jié)論:

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

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

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

           

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

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

           

           

           

           

           

           

           

           

           

           

           

           

           

           

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

          1.void指針是一種特別的指針
             void *vp
            //說它特別是因?yàn)樗鼪]有類型
            //或者說這個類型不能判斷出指向?qū)ο蟮拈L度

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

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

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

          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) 評論(7)  編輯 收藏 引用 所屬分類: C++
           
          評論:
          # re: void指針  Joey Posted @ 2009-07-12 08:17
          寫得不錯,總結(jié)得也還好,  回復(fù)  更多評論  


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

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


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


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


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


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


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

           

           

           

           

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

           

          Void 指針:


           

          Cpp代碼 
          void * pointer_variable; 

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

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

          例如:

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

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

          如果寫成:

          Cpp代碼 
          exf=&f;  

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

          Cpp代碼 
          test=&i;  

          test=&i;  上面一條語句將會報錯。

           

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

          使用前面的例子,如果我們手動聲明一個void類型指針:

          Cpp代碼 
          void* sample;  

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

          Cpp代碼 
          sample=&f;  

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

          Cpp代碼 
          sample=&i;  

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

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

           

          NULL pointer(空指針):

           

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

          舉例:

          Cpp代碼 
          int* exforsys;  
          exforsys=0;  

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

           

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

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

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

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

           

          NULL POINTER ASSIGNMENT

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

           

          Author: UNKNOWN

           

           

           

           

           

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

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

          2.void的含義

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

          void幾乎只有“注釋”和限制程序的作用,因?yàn)閺膩頉]有人會定義一個void變量,讓我們試著來定義:

          void a;

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

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

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

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

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

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

          例如:

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

          其中p1 = p2語句會編譯出錯,提示“'=' : cannot convert from 'int *' to 'float *'”,必須改為:

          p1 = (float *)p2;

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

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

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

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

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

          3.void的使用

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

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

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

          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

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

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

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

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

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

          int function(void)
          {
          return 1;
          }

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

          function(2);

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

          我們在Turbo C 2.0中編譯:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          規(guī)則四如果函數(shù)的參數(shù)可以是任意類型指針,那么應(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 );

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

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

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

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

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

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

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

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

          void的出現(xiàn)只是為了一種抽象的需要,如果你正確地理解了面向?qū)ο笾小俺橄蠡悺钡母拍睿埠苋菀桌斫鈜oid數(shù)據(jù)類型。正如不能給抽象基類定義一個實(shí)例,我們也不能定義一個void(讓我們類比的稱void為“抽象數(shù)據(jù)類型”)變量。

          4.總結(jié)

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

           

           

           


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

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

          void幾乎只有“注釋”和限制程序的作用,因?yàn)閺膩頉]有人會定義一個void變量,讓我們試著來定義:


          void a;

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

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

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

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

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

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

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

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

          容“無類型”。道理很簡單,我們可以說“男人和女人都是人”,但不能說“人是男人”或者“人是女人”。下面的語句編譯出錯:
          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)();
          }

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

           

           

           

           

           

           

           

           

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

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

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

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

          void * 指針

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

          typeof 方式

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


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

          下面來看個實(shí)際的簡單例子吧,例如我希望實(shí)現(xiàn)一個兩個數(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; }


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

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

          posted on 2013-09-10 14:14 小高 閱讀(542) 評論(0)  編輯  收藏 所屬分類: C

          導(dǎo)航

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

          統(tǒng)計(jì)

          常用鏈接

          留言簿(3)

          隨筆分類(352)

          收藏夾(19)

          關(guān)注的blog

          手冊

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 吴堡县| 周宁县| 潍坊市| 汾西县| 错那县| 湖南省| 安徽省| 株洲市| 中阳县| 五华县| 临城县| 辛集市| 平遥县| 卢湾区| 公主岭市| 大足县| 广州市| 遂平县| 广元市| 德兴市| 永吉县| 丰宁| 嘉鱼县| 宁德市| 滦平县| 陆河县| 吉林省| 定西市| 开化县| 宁明县| 山西省| 上杭县| 阳东县| 威宁| 峨眉山市| 黑河市| 新化县| 洛浦县| 宜兴市| 柳河县| 辉南县|