C/C++之数据类型
Tips: 
 ?1. 本人當(dāng)初學(xué)習(xí)C/C++的記錄。 
 ?2. 資源很多都是來自網(wǎng)上的,如有版權(quán)請(qǐng)及時(shí)告知! 
 ?3. 可能會(huì)有些錯(cuò)誤。如果看到,希望能指出,以此共勉!
C數(shù)據(jù)類型
 
 ANSI C99標(biāo)準(zhǔn)中規(guī)定的數(shù)據(jù)類型如下圖所示。 
  
 說明: 
- 同一行類型的語(yǔ)義相同,即可以相互替代。
- long float類型與double相同,故在C99標(biāo)準(zhǔn)中沒有此類型。
- 部分編譯器也提供了unsigned float和unsigned double,最好不要使用,以免影響程序的可移植性。
- int默認(rèn)是signed,所以int, signed, signed int三者等價(jià)。其它unsigned的情況類似。char默認(rèn)情況不確定。
C語(yǔ)言中數(shù)據(jù)類型的長(zhǎng)度
??ANSI C99標(biāo)準(zhǔn)中定義了兩類(四個(gè))類型修飾符:long/short和unsigned/signed。C99標(biāo)準(zhǔn)規(guī)定,long類型不能比變通類型短,short類型不能比普通類型長(zhǎng)。而unsigned與signed的區(qū)別在實(shí)現(xiàn)上是有無符號(hào)的區(qū)別,而是使用上是取值范圍的區(qū)別,兩者表示范圍相同,但前者全是正數(shù),后者關(guān)于0對(duì)稱。 
 說明: 
- long/short可以修飾int,long還可以修飾double。
- unsigned/signed可以修飾int、char,不可以修飾浮點(diǎn)型。
- int長(zhǎng)度是機(jī)器的字長(zhǎng),short int是半個(gè)字長(zhǎng),long int是一個(gè)或兩個(gè)字長(zhǎng)。
- unsigned/signed長(zhǎng)度與普通類型一樣,只是表示區(qū)間不同。
C語(yǔ)言中數(shù)據(jù)類型的轉(zhuǎn)換
類型轉(zhuǎn)換分為顯示和隱式兩種,前者比較簡(jiǎn)單,這里只講后者。下面是C99標(biāo)準(zhǔn)中給出的各種類型對(duì)象的基本轉(zhuǎn)換規(guī)則:
- 枚舉常量: 轉(zhuǎn)換成int,如超出int范圍,則轉(zhuǎn)成long int等
- 浮點(diǎn)型: 
 - 如果轉(zhuǎn)成整類型,只保留整數(shù)部分,如果超出整型表示范圍,則轉(zhuǎn)換錯(cuò)誤;
- 如果向上轉(zhuǎn)成double/long double,值不變;
- 如果向下轉(zhuǎn)成float/double等,如果能用float/double表示,則正常,如果超出表示范圍,則轉(zhuǎn)換錯(cuò)誤,而如果在表示范圍內(nèi),但精度降低,則要依賴于編譯器的處理了
 
- 整型: short int/char/枚舉類型/位類型都可轉(zhuǎn)換成int,如果超出int表示范圍,則提升到unsigned int。 
 對(duì)于二元運(yùn)算符中的普通算術(shù)運(yùn)算轉(zhuǎn)換,C99標(biāo)準(zhǔn)給出了如下圖所示的轉(zhuǎn)換規(guī)則:
 
 說明:
- 對(duì)于unsigned char和unsigned short int的整型提升問題,C99標(biāo)準(zhǔn)給出“保值”的轉(zhuǎn)換方法:方法是將unsigned char和unsigned short int轉(zhuǎn)換成int,如果超出表示范圍,則轉(zhuǎn)成unsigned int。 
- 對(duì)于表格中第五行,long int與unsigned int的情況,在vc6.0沒有這樣實(shí)現(xiàn),是直接轉(zhuǎn)成unsigned int。
C++數(shù)據(jù)類型
??面向?qū)ο缶幊?#xff08;OOP)的本質(zhì)是設(shè)計(jì)并擴(kuò)展自己的數(shù)據(jù)類型。C++兼容C的數(shù)據(jù)類型,又稍有區(qū)別。當(dāng)然,下面的數(shù)據(jù)類型說明多數(shù)同樣適用于C類型,部分不同于C的地方將單獨(dú)指出。
基本類型
??基本類型主要就是整型和浮點(diǎn)型,同時(shí)對(duì)這兩種進(jìn)行了多種變形。(C++11新增了bool類型,兼容C99)
整型
??基本整型包括:char、short、int、long和C++11中新增的long long(兼容C99)。其中每種類型都有有符號(hào)版和無符號(hào)版。 
 1. 每種數(shù)據(jù)類型都有一定的數(shù)據(jù)范圍,不同的系統(tǒng)可能有不能的范圍。C++采用靈活的標(biāo)準(zhǔn),確保了數(shù)據(jù)類型的最小長(zhǎng)度(C語(yǔ)言可能不同):
- short 至少16位
- int 至少與short一樣長(zhǎng)
- long 至少32位,且至少與int一樣長(zhǎng)
- long long 至少64位,且至少與long一樣長(zhǎng) 
 在頭文件climits(舊limits.h)中包含了關(guān)于整型限制的信息。如下圖(VS2010):
 
 數(shù)據(jù)類型的范圍是怎么算出來的。(C及C++)
- 計(jì)算機(jī)中數(shù)據(jù)都是以二進(jìn)制存儲(chǔ);
- 二進(jìn)制可以由不同的編碼(原碼、補(bǔ)碼、反碼)表示,計(jì)算機(jī)統(tǒng)一采用補(bǔ)碼表示。
- 計(jì)算機(jī)中的正負(fù)號(hào),0表示正數(shù),1表示負(fù)數(shù) 
 以2字節(jié)(16位)有符號(hào)類型來說:
 范圍: (10進(jìn)制)-32768到32767;(16進(jìn)制)8000到7FFF;(2進(jìn)制補(bǔ)碼)1000,0000,0000 ,0000到0111,1111,1111,1111
 原碼:最高位為符號(hào)位,其余位與正常二進(jìn)制表示方法一致;
 原碼表示范圍:
 最大為0111111111111111 = 2^15-1 = 32767
 最小為1111111111111111 = -2^15-1 = -32767
 +0和-0:
 +0:0000000000000000
 -0:1000000000000000
 即正零與負(fù)零表示方法不同。也就意味著:原碼能表示的有符號(hào)數(shù)范圍是:-32767~-0和0~32767
 補(bǔ)碼: 正數(shù)補(bǔ)碼與原碼相同,負(fù)數(shù)補(bǔ)碼需要把除符號(hào)位以外的原碼取反加1
 補(bǔ)碼表示范圍:
 最大為0111111111111111 = 2^15-1 = 32767
 最小為1000000000000001 = 原碼:1111111111111111的符號(hào)不變其余取反為:1000000000000000,再加1為:1000000000000001
 +0和-0:
 +0:0000000000000000 // 與原碼相同
 -0:0000000000000000 // 原碼:1000000000000000的符號(hào)不變其余取反為:1111111111111111,再加1為:0000000000000000(進(jìn)位舍掉)
 也就是正0和負(fù)0在補(bǔ)碼系統(tǒng)中的編碼是一樣的。也就是補(bǔ)碼會(huì)比原碼多一個(gè)編碼出來,這個(gè)編碼就是1000000000000000。因?yàn)槿魏我粋€(gè)原碼都不可能在轉(zhuǎn)成補(bǔ)碼時(shí)變成1000000000000000。所以,人為規(guī)定1000000000000000這個(gè)補(bǔ)碼編碼為-32768。所以,補(bǔ)碼系統(tǒng)中,范圍是-23768~32767。
 
 
- C++中,對(duì)于變量的賦值更加靈活,原來用于結(jié)構(gòu)體和數(shù)組的賦值,現(xiàn)在對(duì)于單個(gè)變量也是可以的。 
 
- C++如何確定整型常數(shù)的類型
- 對(duì)于有字符修飾的整型數(shù)字,根據(jù)字符來判斷類型。例如:123L,則為long;123uL為unsigned long。
- 對(duì)于沒有符號(hào)修飾的十進(jìn)制數(shù)字,C++總是采用int、unsigned int、long、unsigned long、long long (沒有short)中能夠存儲(chǔ)該數(shù)的最小類型表示。
- 對(duì)于沒有符號(hào)修飾的十六進(jìn)制或者八進(jìn)制,則總是以對(duì)應(yīng)的無符號(hào)類型表示。 
 
浮點(diǎn)型
C++基本浮點(diǎn)型包括:float、double、long double。unsigned和signed不能修飾浮點(diǎn)型 
 C/C++對(duì)于浮點(diǎn)型有效位的規(guī)定:
- float至少32位
- double 至少48位,且不少于float
- long double 至少和double一樣多
注意:默認(rèn)情況下,浮點(diǎn)常數(shù)字為double型,例如程序中直接寫1.0,其被當(dāng)做double型數(shù)字。
在頭文件cfloat(舊float.h)中包含了關(guān)于浮點(diǎn)型限制的信息(有些系統(tǒng)沒有提供該文件)。如下圖: 
  
 從上表中可看到,在VS2010中,double的有效位數(shù)為15,float的有效位數(shù)為6 
 
浮點(diǎn)數(shù)的存儲(chǔ)
??C/C++編譯器都是按照IEEE的浮點(diǎn)數(shù)表示法,即一種科學(xué)計(jì)數(shù)法,用符號(hào)、指數(shù)和尾數(shù)來表示,底數(shù)為2。也就是把浮點(diǎn)數(shù)表示為尾數(shù)乘以2的指數(shù)次方再添加上符號(hào)的形式。因?yàn)榭茖W(xué)技術(shù)法 a×bm的形式,a介于1~10,而浮點(diǎn)數(shù)表示法中,a始終為1,所以在最終的表示結(jié)果中,這個(gè)1被略去。即:尾數(shù)二進(jìn)制最高位的1不要 
 具體規(guī)格是:
| float | 1 | 8 | 23 | 32 | 
| double | 1 | 11 | 52 | 64 | 
下面通過例子來解釋上面的表示規(guī)格:
- 38414.4表示為double: 
 - 分開整數(shù)和小數(shù)部分,整數(shù)化為16進(jìn)制,0x960E;小數(shù)部分為:0.4=0.5×0+0.25×1+0.125×1+……+0.5×(1 or 0)/n+……。//實(shí)際上這永遠(yuǎn)算不完!
- 有的小數(shù)可以窮盡,有的是永遠(yuǎn)不會(huì)窮盡的,此時(shí)只需要提取出各項(xiàng)的系數(shù),即011……,這些項(xiàng)的和加上整數(shù)部分共53位就可以了。正如上面所言的,最高為不變的1可以省略(歸一化),最終是53-1=52位。
- 38414.4可以表示為1001011000001110.0110011001100110011001100110011001100B。
- 用科學(xué)計(jì)數(shù)法表示為1.0010110000011100110011001100110011001100110011001100×215。
- 然后計(jì)算階碼,階碼共11位,可以表示-1024~1023,因?yàn)橹笖?shù)可以為負(fù)數(shù),規(guī)定先加上1023變?yōu)榉秦?fù)數(shù)(指數(shù)偏移),上面的15表示為15+1023=1038,二進(jìn)制為10000001110。符號(hào)位,0為正,1為負(fù)。所以最終結(jié)果是
- 0 10000001110 0010110000011100110011001100110011001100110011001100
- 顏色與上表對(duì)應(yīng)。
 
- 3490593表示為float: 
 - 3490593的浮點(diǎn)數(shù)為3490593.0。
- 整數(shù)化為二進(jìn)制,為1101010100001100100001B,即1.101010100001100100001×221,由于float的尾數(shù)有23位,需要補(bǔ)0。即1.10101010000110010000100×221。
- 計(jì)算階碼時(shí),類似double的表示,階碼共8位,表示的范圍是-128~127,為了方便,加上127,上面的21表示為21+127=148=10010100B。最終結(jié)果是:
- 0 10010100 10101010000110010000100
- 顏色與上表對(duì)應(yīng)。
 
- 0.5的二進(jìn)制表示: 
 - 上面給出了0.4的二進(jìn)制表示的計(jì)算方法:
- 0.4 = 0.5×0+0.25×1+0.125×1+……+0.5×(1 or 0)/n+……。
- 它是無窮盡的,直到精度合適了為止。
- 然而對(duì)于有的數(shù)來說,是有窮的,比如0.5=1×0.5。整數(shù)部分為0,小數(shù)部分為0.1,所以0.5的二進(jìn)制形式是0.1,即1.0 × 2-1。
- 計(jì)算階碼時(shí),用127+(-1)=126=b1111110B。所以最終結(jié)果是:
- 0 01111110 00000000000000000000000
- 顏色與上表對(duì)應(yīng)。
 
- -12.5的二進(jìn)制浮點(diǎn)表示: 
 - 整數(shù)部分為12,即1100B;小數(shù)部分為0.5,即0.1B,即1100.10000000000000000000,即1.10010000000000000000000 × 23。
- 計(jì)算階碼,3+127=130,即10000010B,所以最終結(jié)果是:
- 1 10000010 10010000000000000000000
- 顏色與上表對(duì)應(yīng)。
 
- 逆向求取,1011 1101 0100 0000 0000 0000 0000 0000轉(zhuǎn)為十進(jìn)制: 
 - 1011 1101 0100 0000 0000 0000 0000 0000為:
- 1 01111010 10000000000000000000000
- 所以該數(shù)為-1.10000000000000000000000 × 201111010-127=-5 = -0.000011B = 0.046875 
 詳細(xì)見http://blog.163.com/yql_bl/blog/static/847851692008112013117685/
 有了以上知識(shí),那么printf(“%f”,10/3);的結(jié)果是什么?結(jié)果是0.0000
 10/3的結(jié)果無疑應(yīng)該是3,但是,我們卻要求printf按照浮點(diǎn)數(shù)來去這個(gè)數(shù),通過以上我們知道,整數(shù)和浮點(diǎn)數(shù)的存儲(chǔ)方式是不一樣的。
 整型數(shù)3在內(nèi)存存儲(chǔ)如下:
 0000 0000 0000 0000 0000 0000 0000 0011
 但是現(xiàn)在我們要用浮點(diǎn)數(shù)的方式來解析這32位數(shù)字。按照浮點(diǎn)數(shù)方式:
 0000 0000 0000 0000 0000 0000 0000 0011
 上面紅色是符號(hào)為0,表示正數(shù);藍(lán)色的是指數(shù)位,結(jié)果為0,但是這兒要注意的一點(diǎn)是指數(shù)在存儲(chǔ)的時(shí)候是進(jìn)行過偏移的,所以這兒要剪掉127,所以指數(shù)為-127。最后的紫色是尾數(shù),結(jié)果是2^(-22)+2^(-23),但是也要注意一點(diǎn)是,尾數(shù)在進(jìn)行存儲(chǔ)的時(shí)候是歸一化過的,小數(shù)點(diǎn)前面其實(shí)有個(gè)1,所以最后尾數(shù)是1+2^(-22)+2^(-23)。所以最后的浮點(diǎn)數(shù)是:[1+2^(-22)+2^(-23)]*2^(-127)轉(zhuǎn)化為可讀數(shù)字就是5.87747385606e-39 ,這個(gè)數(shù)就非常小了,所以顯示的時(shí)候就是0.000000啦。
 
數(shù)據(jù)類型轉(zhuǎn)換
隱式類型轉(zhuǎn)換
在某些情況下,C++將自動(dòng)對(duì)數(shù)據(jù)類型進(jìn)行轉(zhuǎn)換:
- 不同數(shù)據(jù)類型之間的賦值 例如:int a = 1.0;目標(biāo)類型是被賦值對(duì)象的類型。
- 算數(shù)表達(dá)式中存在不同數(shù)據(jù)類型的數(shù)運(yùn)算,例如int a = 1;double b = 2.0; int c = a+b;
- 將一個(gè)表達(dá)式作為實(shí)參傳遞給函數(shù)調(diào)用,此時(shí)形參和實(shí)參類型不一致:目標(biāo)轉(zhuǎn)換類型為形參的類型
- 從一個(gè)函數(shù)返回一個(gè)表達(dá)式,表達(dá)式類型與返回類型不一致:目標(biāo)轉(zhuǎn)換類型為函數(shù)的返回類型 
 C++11表達(dá)中,不同數(shù)據(jù)進(jìn)行運(yùn)算時(shí)的校驗(yàn)規(guī)則(與C語(yǔ)言稍有區(qū)別):
- 如果其中有一個(gè)數(shù)為long double,那么另一個(gè)就被轉(zhuǎn)換為long double
- 否則,如果有一個(gè)數(shù)double,那么另一個(gè)就被轉(zhuǎn)換為double
- 否則,如果有一個(gè)數(shù)為float,那么另一個(gè)數(shù)就被轉(zhuǎn)換為float
- 否則,否則說明操作數(shù)都是整型,執(zhí)行整型提升
- 如果兩個(gè)操作數(shù)同是有符號(hào)或同是無符號(hào),這轉(zhuǎn)換為等級(jí)較高的類型進(jìn)行運(yùn)算
- 如果一個(gè)有符號(hào)一個(gè)無符號(hào),且無符號(hào)類型級(jí)別高,這轉(zhuǎn)換無符號(hào)數(shù)運(yùn)算
- 否則,如果有符號(hào)可以表示所有無符號(hào)取值,這轉(zhuǎn)換為有符號(hào)類型運(yùn)算
- 否則,將兩個(gè)數(shù)都轉(zhuǎn)換為有符號(hào)數(shù)的無符號(hào)版本運(yùn)算 
 
顯示類型轉(zhuǎn)換
(typename)value; // C語(yǔ)言風(fēng)格 typename(value); // C++風(fēng)格此外,C++還提供了四個(gè)關(guān)鍵字來實(shí)現(xiàn)轉(zhuǎn)換,與傳統(tǒng)強(qiáng)制轉(zhuǎn)換相比,其轉(zhuǎn)換更加嚴(yán)格
static_cast <type-id> (expression); //該運(yùn)算符把expression轉(zhuǎn)換為type-id類型,但沒有運(yùn)行時(shí)類型檢查來保證轉(zhuǎn)換的安全性。 dynamic_cast <type-id> (expression); //該運(yùn)算符把expression轉(zhuǎn)換成type-id類型的對(duì)象。Type-id必須是類的指針、類的引用或者void *;如果type-id是類指針類型,那么expression也必須是一個(gè)指針,如果type-id是一個(gè)引用,那么expression也必須是一個(gè)引用。 reinpreter_cast <type-id> (expression); // type-id必須是一個(gè)指針、引用、算術(shù)類型、函數(shù)指針或者成員指針。它可以把一個(gè)指針轉(zhuǎn)換成一個(gè)整數(shù),也可以把一個(gè)整數(shù)轉(zhuǎn)換成一個(gè)指針(先把一個(gè)指針轉(zhuǎn)換成一個(gè)整數(shù),在把該整數(shù)轉(zhuǎn)換成原類型的指針,還可以得到原先的指針值) const_cast<type_id> (expression); // 用來修改類型的const或volatile屬性。除了const 或volatile修飾之外, type_id和expression的類型是一樣的。顯示對(duì)浮點(diǎn)數(shù)進(jìn)行強(qiáng)制轉(zhuǎn)換時(shí),規(guī)則如下:
| 浮點(diǎn)型轉(zhuǎn)整型 | 小數(shù)被省略,如果超出float范圍,結(jié)果不確定 | 
| 較大整型轉(zhuǎn)較小整型,例如long轉(zhuǎn)int | 如果超出int范圍,通常只復(fù)制右邊的值 | 
潛在的數(shù)據(jù)轉(zhuǎn)換問題
復(fù)合類型
復(fù)合數(shù)據(jù)類型由基本數(shù)據(jù)類型組成,C++中類就是一種符合數(shù)據(jù)類型。此外數(shù)組、字符串、結(jié)構(gòu)體、共同體、枚舉、指針和自由存儲(chǔ)空間都作為復(fù)合數(shù)據(jù)類型
數(shù)組(C/C++)
如果只對(duì)數(shù)組部分賦值,后面的默認(rèn)為零。 
 
不能將一個(gè)數(shù)組賦值給另一個(gè)數(shù)組。
指針和二維數(shù)組(C/C++)
指針
首先,指針也是一個(gè)變量,其在內(nèi)存中一般占用4個(gè)字節(jié)。比較特殊的是,指針變量中存放的值是一個(gè)地址。例如: 
 int *p; 
 這里,定義了一個(gè)指針,編譯器在內(nèi)存中拿出4個(gè)字節(jié),名字叫p,里面存放一個(gè)4字節(jié)的地址。對(duì)于未初始化的指針,其值是隨機(jī)的,很危險(xiǎn)! 
 常見的指針操作:*與++、– 
指針和引用的聯(lián)系與區(qū)別(僅C++) 
 (1)指針是一個(gè)實(shí)體,而引用僅是個(gè)別名; 
 (2)引用使用時(shí)無需解引用(*),指針需要解引用; 
 (3)引用只能在定義時(shí)被初始化一次,之后不可變;指針可變; 
 (4)引用沒有 const,指針有 const; 
 (5)引用不能為空,指針可以為空; 
 (6)“sizeof 引用”得到的是所指向的變量(對(duì)象)的大小,而“sizeof 指針”得到的是指針本身(所指向的變量或?qū)ο蟮牡刂?的大小; 
 (7)指針和引用的自增(++)運(yùn)算意義不一樣; 
 (8)從內(nèi)存分配上看:程序?yàn)橹羔樧兞糠峙鋬?nèi)存區(qū)域,而引用不需要分配內(nèi)存區(qū)域。 
 在說明指針的時(shí)候,有必要額外說明一下二維數(shù)組。
以上的輸出結(jié)果為:0 0 1 1
二維數(shù)組
??有很多地方說數(shù)組就是指針,這是錯(cuò)誤的一種說法。這兩者是不同的數(shù)據(jù)結(jié)構(gòu)。其實(shí),在C/C++中沒有所謂的二維數(shù)組,書面表達(dá)就是數(shù)組的數(shù)組。為了表述方便才叫它二維數(shù)組。二維數(shù)組在概念上是二維的,即其下標(biāo)在兩個(gè)方向上變化,下標(biāo)變量在數(shù)組中的位置也處于一個(gè)平面之中,而不是像一維數(shù)組只是一個(gè)向量。但是,實(shí)際的硬件存儲(chǔ)器卻是連續(xù)編址的,也就是說存儲(chǔ)器單元是按一維線性排列的。如何在一維存儲(chǔ)器中存放二維數(shù)組,可有兩種方式:一種是按行排放, 即放完一行之后順次放入第二行。另一種是按列排放, 即放完一列之后再順次放入第二列。 
 ??在C語(yǔ)言中,二維數(shù)組是按行排列的。即,先存放a[0]行,再存放a[1]行,最后存放a[n]行。每行中的元素也是依次存放。例如對(duì)數(shù)組a[5][3]賦值兩種方式(結(jié)果完全相同): 
- 按行分段賦值可寫為: 
 int a[5][3]={ {80,75,92}, {61,65,71}, {59,63,70}, {85,87,90}, {76,77,85} };
- 按行連續(xù)賦值可寫為: 
 int a[5][3]={ 80,75,92,61,65,71,59,63,70,85,87,90,76,77,85};
 注意:
 
- 可以只對(duì)部分元素賦初值,未賦初值的元素自動(dòng)取0值。
- 如果對(duì)全部元素賦初值,則第一維的長(zhǎng)度可以不給出,例如:int a[][3]={1,2,3,4,5,6,7,8,9};
二維數(shù)組一維化
??我們可以用一個(gè)指向int型的指針變量來訪問這個(gè)數(shù)組,下面的代碼是將數(shù)組一維化(以上面的a數(shù)組為例):
int *p = a[0]; // 這樣就可以用 p 訪問每個(gè)元素了 p[3] // 第三個(gè)元素 *(p+3) // 這個(gè) = p[3]這樣就實(shí)現(xiàn)了將二維數(shù)組一維化,通過p訪問的是每個(gè)元素,而不是行
數(shù)組指針和指針數(shù)組
指針數(shù)組: 指針數(shù)組就是個(gè)數(shù)組,只不過元素是指針。定義方式如:int *p[3]; 表示三個(gè)指針,分別為:p[0]、p[1]、p[2] 
 數(shù)組指針: 指向數(shù)組的指針。定義方式如:int (*p)[3]; 表示 p指向的是一個(gè)數(shù)組元素為int類型并且數(shù)組元素的個(gè)數(shù)為3的一個(gè)指針。 
 
上例中,pArr是個(gè)數(shù)組指針,每次+1是移動(dòng)一行,不是一個(gè)元素。比如說,pArr+1代表的現(xiàn)在指針已經(jīng)指向第一行元素了(0行開始),而要取得指針?biāo)傅膶?duì)象,就要用到解引用運(yùn)算符,所以(pArr+1)就代表第一行數(shù)組,是整個(gè)這一行元素就取到了,那現(xiàn)在要取這一行的第二個(gè)元素,只須將指針再移動(dòng)兩個(gè)元素,即*(iArr+1) + 2,這樣就指向了這個(gè)元素的地址,再解引用取得元素的值即可。 
  
 也許我們應(yīng)該這樣來數(shù)組指針: 
 int (*)[10] p2; 
 int (*)[10]是指針類型,p2 是指針變量。這樣看起來的確不錯(cuò),不過就是樣子有些別扭。其實(shí)數(shù)組指針的原型確實(shí)就是這樣子的,只不過為了方便與好看把指針變量p2 前移了而已。 
 既然這樣,那問題就來了。現(xiàn)在再來看看下面的代碼:
上面對(duì)p3 和p4 的使用,哪個(gè)正確呢?p3+1 的值會(huì)是什么?p4+1 的值又會(huì)是什么? 
 毫無疑問,p3 和p4 都是數(shù)組指針,指向的是整個(gè)數(shù)組。&a 是整個(gè)數(shù)組的首地址,a是數(shù)組首元素的首地址,其值相同但意義不同。在C 語(yǔ)言里,賦值符號(hào)“=”號(hào)兩邊的數(shù)據(jù)類型必須是相同的,如果不同需要顯示或隱式的類型轉(zhuǎn)換。p3 這個(gè)定義的“=”號(hào)兩邊的數(shù)據(jù)類型完全一致,而p4 這個(gè)定義的“=”號(hào)兩邊的數(shù)據(jù)類型就不一致了。左邊的類型是指向整個(gè)數(shù)組的指針,右邊的數(shù)據(jù)類型是指向單個(gè)字符的指針。在Visual C++6.0 上給出如下警告: 
 warning C4047: 'initializing' : 'char (*)[5]' differs in levels of indirection from 'char *'。 
 還好,這里雖然給出了警告,但由于&a 和a 的值一樣,而變量作為右值時(shí)編譯器只是取變量的值,所以運(yùn)行并沒有什么問題。不過我仍然警告你別這么用。 
 但是如果修改一下代碼,把數(shù)組大小改小點(diǎn),會(huì)有什么問題?p3+1 和p4+1 的值又是多少呢?
或把數(shù)組大小改大點(diǎn):
int main() {char a[5]={'A','B','C','D'};char (*p3)[10] = &a;char (*p4)[10] = a;return 0; }測(cè)試結(jié)果:把數(shù)組大小改變,都會(huì)編譯不通過。
地址的強(qiáng)制轉(zhuǎn)換
以下,以x86 Windows為例
#include <stdio.h> int main() {int a[4]={1,2,3,4};int *ptr1=(int *)(&a+1);int *ptr2=(int *)((int)a+1);printf("%x,%x",ptr1[-1],*ptr2);return 0; }下面分析上面的數(shù)據(jù)結(jié)果 
  
 ptr1: a為數(shù)組名,那么&a+1不是增一個(gè)int,而是(int*)(a的地址+sizeof(a)),因此ptr1指向了數(shù)組結(jié)尾的第一個(gè)字節(jié)。 
 可以這樣理解:不管是增1還是減1,這里的1都是sizeof(類型),上面對(duì)a取地址,可認(rèn)為此時(shí)類型為int a[4],這里增的是sizeof(a) 
 ptr2: 任何數(shù)值一旦被強(qiáng)制轉(zhuǎn)換,其類型就改變了。這里實(shí)際上就是將地址a,轉(zhuǎn)換為了數(shù),然后+1,把轉(zhuǎn)換后的數(shù)再次轉(zhuǎn)換為地址。如下圖: 
 
字符串
C++有兩種風(fēng)格的字符串,一種是C語(yǔ)言風(fēng)格的,一種是C++語(yǔ)言風(fēng)格string。
C語(yǔ)言風(fēng)格字符串
C語(yǔ)言風(fēng)格的字符串以空字符結(jié)尾,空字符被寫作\0,ASCII碼為0。C不會(huì)檢查字符串長(zhǎng)度是否越界。 
 對(duì)于C風(fēng)格的字符串操作一般通過庫(kù)函數(shù)來實(shí)現(xiàn),在頭文件string.h中(C++ cstring)包換大量字符串操作的函數(shù)。 
 
要保證目的字符串可以容納原字符串,否則,編譯不會(huì)出錯(cuò),但是運(yùn)行時(shí),會(huì)出現(xiàn)錯(cuò)誤:Stack around the variable ‘xxx’ was corrupted.
C++字符串 String
IOS/ANSI C++98標(biāo)準(zhǔn)添加了String類,使用者可以直接將它作為一種數(shù)據(jù)類型來用,定義字符串變量(對(duì)象)。要使用String類必須包含頭文件string,而且string位于std命名空間中。 
 詳細(xì)的使用方法見文件: 雙擊圖標(biāo)查看!
結(jié)構(gòu)體
無論在C還是C++中,結(jié)構(gòu)體都是很常用的一種數(shù)據(jù)類型。結(jié)構(gòu)體名,用作結(jié)構(gòu)體類型的標(biāo)志,它又稱結(jié)構(gòu)體標(biāo)記。大括號(hào)內(nèi)是該結(jié)構(gòu)體中的成員列表,又稱為域表。
結(jié)構(gòu)體的內(nèi)存對(duì)齊
結(jié)構(gòu)體內(nèi)存分配的原則:編譯器按照成員列表順序一個(gè)接一個(gè)地給每個(gè)成員分配內(nèi)存。只有當(dāng)存儲(chǔ)成員需要滿足正確的邊界對(duì)齊要求時(shí),成員之間才可能出現(xiàn)用于填充的額外內(nèi)存空間。如果不按照平臺(tái)要求對(duì)數(shù)據(jù)存放進(jìn)行對(duì)齊,會(huì)帶來存取效率上的損失。此外,合理利用字節(jié)對(duì)齊還可以有效地節(jié)省存儲(chǔ)空間。但要注意,在32位機(jī)中使用1字節(jié)或2字節(jié)對(duì)齊,反而會(huì)降低變量訪問速度。因此需要考慮處理器類型。還應(yīng)考慮編譯器的類型。在VC/C++和GNU GCC中都是默認(rèn)是4字節(jié)對(duì)齊。 
 結(jié)構(gòu)體字節(jié)對(duì)齊的細(xì)節(jié)和具體編譯器實(shí)現(xiàn)相關(guān),但一般而言滿足三個(gè)準(zhǔn)則: 
 1) 結(jié)構(gòu)體變量的首地址能夠被其最寬基本類型成員的大小所整除; 
 2) 結(jié)構(gòu)體每個(gè)成員相對(duì)結(jié)構(gòu)體首地址的偏移量(offset)都是成員大小的整數(shù)倍,如有需要編譯器會(huì)在成員之間加上填充字節(jié)(internal adding); 
 3) 結(jié)構(gòu)體的總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍,如有需要編譯器會(huì)在最末一個(gè)成員之后加上填充字節(jié){trailing padding}。
位域(位段)
有些信息在存儲(chǔ)時(shí),并不需要占用一個(gè)完整的字節(jié), 而只需占幾個(gè)或一個(gè)二進(jìn)制位。例如在存放一個(gè)開關(guān)量時(shí),只有0和1 兩種狀態(tài), 用一位二進(jìn)位即可。為了節(jié)省存儲(chǔ)空間,并使處理簡(jiǎn)便,C語(yǔ)言又提供了一種數(shù)據(jù)結(jié)構(gòu),稱為“位域”或“位段”。所謂“位域”是把一個(gè)字節(jié)中的二進(jìn)位劃分為幾個(gè)不同的區(qū)域, 并說明每個(gè)區(qū)域的位數(shù)。每個(gè)域有一個(gè)域名,允許在程序中按域名進(jìn)行操作。 這樣就可以把幾個(gè)不同的對(duì)象用一個(gè)字節(jié)的二進(jìn)制位域來表示。定義方式如下:
struct 位域結(jié)構(gòu)名 { 類型說明符 位域名:位域長(zhǎng)度 }; 例: struct bs {int a:8; // 8個(gè)二進(jìn)制位int b:2; // 2個(gè)二進(jìn)制位int c:6; // 6個(gè)二進(jìn)制位 };位域需要遵循以下規(guī)則: 
 1. 位域的長(zhǎng)度不能大于數(shù)據(jù)類型本身的長(zhǎng)度,比如int類型就能超過32位二進(jìn)位。有其他人說是不能超過8位,我在我的機(jī)子上是可以實(shí)現(xiàn)int :32的位域長(zhǎng)度的。 
 2. 位域可以無位域名,這時(shí)它只用來作填充或調(diào)整位置。無名的位域是不能使用的 
 3. 如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的字段將緊鄰前一個(gè)字段存儲(chǔ),直到不能容納為止; 
 4. 如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲(chǔ)單元開始,其偏移量為其類型大小的整數(shù)倍; 
 5. 如果相鄰的位域字段的類型不同,則各編譯器的具體實(shí)現(xiàn)有差異,VC6采取不壓縮方式(不同位域字段存放在不同的位域類型字節(jié)中),Dev-C++和GCC都采取壓縮方式; 
 6. 如果位域字段之間穿插著非位域字段,則不進(jìn)行壓縮 
 7. 整個(gè)結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍 
 8. C99規(guī)定int、unsigned int和bool可以作為位域類型
系統(tǒng)會(huì)先為結(jié)構(gòu)體成員按照對(duì)齊方式分配空間和填塞(padding),然后對(duì)變量進(jìn)行位域操作。
舉例如下:
#include <iostream> #include <memory.h>using namespace std; struct A {int a:5;int b:3; }; int main(void) {char str[100] = "0134324324afsadfsdlfjlsdjfl";struct A d;memcpy(&d, str, sizeof(A));cout << d.a << endl;cout << d.b << endl;return 0; }如上代碼,執(zhí)行結(jié)果如下:
分析: 
 高位 00110100 00110011 00110001 00110000 低位 
 ‘4’ ‘3’ ‘1’ ‘0’ // 以上二進(jìn)制位字符的ASCII碼 
 其中d.a和d.b共同占用低位一個(gè)字節(jié)(00110000), d.a : 10000, d.b : 001 
 然后,int 是有符號(hào)的。所以d.a對(duì)應(yīng)的數(shù)為11111111 11111111 11111111 11110000;d.b對(duì)應(yīng)的二進(jìn)制為10000000 00000000 00000000 00000001 
 同理,如果int a:5改為了int a:16,此時(shí),d.a對(duì)應(yīng)的值就是10000000 00000000 00110001 00110000
共同體(聯(lián)合體)
共同體是一種數(shù)據(jù)格式,它能夠存儲(chǔ)不同的數(shù)據(jù)類型,但同時(shí)只能存儲(chǔ)一種。 
 匿名共同體:定義時(shí),直接省去共同體的名稱,但這里一般同時(shí)定義一個(gè)對(duì)象,因?yàn)闆]有名字以后就沒法定義了!除非是放在其他結(jié)構(gòu)里面,可以不定義對(duì)象。 
  
 關(guān)于共同體的嵌套 
 
注:結(jié)構(gòu)體與聯(lián)合體有何區(qū)別?
枚舉
1、枚舉值默認(rèn)從零開始,后面的比前面的增加1 
 2、C早期版本規(guī)定,枚舉賦值必須是int型,現(xiàn)在該限制被取消了,賦值可以是long、long long 
 3、可以定義具有相同值的枚舉值 
 在C++98中enum變量的實(shí)際大小由編譯器決定,只要能夠保存enum的成員即可,而在將要發(fā)布的新的C++0x中,可以指定enum的實(shí)際實(shí)現(xiàn)類型,如實(shí)現(xiàn)為int類型。 
 enum Month:int{ Jan, Feb, …, Dec } 
 
其他類型:自由存儲(chǔ)
單獨(dú)說明
總結(jié)
以上是生活随笔為你收集整理的C/C++之数据类型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: C/C++ 之 C发展史及 各标准特性说
- 下一篇: C/C++之常用关键字
