
之前寫過一篇關(guān)于??C語(yǔ)言內(nèi)存管理??的文章,對(duì)在C語(yǔ)言中使用內(nèi)存中需要注意的一些問題和解決辦法做了一些總結(jié)。實(shí)際上,內(nèi)存終歸是要存儲(chǔ)數(shù)據(jù)的,這次對(duì)C語(yǔ)言中的數(shù)據(jù)存儲(chǔ)做一些討論。
(資料圖)
本文結(jié)構(gòu):
C語(yǔ)言數(shù)據(jù)類型可以分為兩大類:內(nèi)置數(shù)據(jù)類型和構(gòu)造數(shù)據(jù)類型
內(nèi)置類型包含整形家族、浮點(diǎn)型、指針類型和空類型;構(gòu)造數(shù)據(jù)類型可以由基本數(shù)據(jù)類型進(jìn)行組合以實(shí)現(xiàn)數(shù)據(jù)類型的自定義,包含數(shù)組、結(jié)構(gòu)體、枚舉和聯(lián)合。
整型家族包含各種類型的??int?
??類型,因?yàn)??char?
??類型在內(nèi)存中以??ASCII?
??碼值的形式存儲(chǔ),所以也被歸為整型家族;浮點(diǎn)型包含??float?
??和??double?
?類型;指針類型種類較多,可以是任意內(nèi)置類型的,指針類型的意義主要是解引用時(shí)訪問空間的大小和決定指針加一的步長(zhǎng);??void?
?類型常見于函數(shù)的參數(shù)和返回值。
構(gòu)造數(shù)據(jù)類型相對(duì)自由,可以由不同內(nèi)置數(shù)據(jù)類型進(jìn)行構(gòu)造。關(guān)于??構(gòu)造數(shù)據(jù)類型??相對(duì)詳細(xì)的討論可以參考我之前的一篇文章。
為什么數(shù)組屬于構(gòu)造數(shù)據(jù)類型:
#includeint main(){ int arr1[10] = { 0 };//數(shù)組arr1的類型為 int [10] int arr2[11] = { 0 };//數(shù)組arr2的類型為 int [11] return 0;}
數(shù)據(jù)類型的意義主要體現(xiàn)在存儲(chǔ)數(shù)據(jù)和讀取數(shù)據(jù)兩個(gè)角度。
一方面,存儲(chǔ)數(shù)據(jù)時(shí),可以明確所需存儲(chǔ)空間的大小,以更有效地利用內(nèi)存空間;另一方面,在讀取數(shù)據(jù)時(shí),能夠明確如何看待內(nèi)存塊,準(zhǔn)確地將數(shù)據(jù)讀出。
作為內(nèi)置數(shù)據(jù)類型最龐大的家族,整形的存儲(chǔ)相對(duì)簡(jiǎn)單。整型在內(nèi)存中以補(bǔ)碼存儲(chǔ),正數(shù)的原碼反碼補(bǔ)碼相同,負(fù)數(shù)需要另做計(jì)算。
原碼即十進(jìn)制數(shù)直接轉(zhuǎn)化為二進(jìn)制之后的二進(jìn)制序列;原碼的符號(hào)位不變,其他位按位取反得到反碼;反碼加一得到補(bǔ)碼。
規(guī)定正數(shù)的原碼反碼補(bǔ)碼相同,補(bǔ)碼實(shí)際上是為了存儲(chǔ)負(fù)數(shù)和進(jìn)行減法運(yùn)算而存在的。因?yàn)?strong>CPU只有加法器,計(jì)算減法時(shí)實(shí)際上計(jì)算的是正數(shù)加負(fù)數(shù),如果直接將兩者的原碼形式相加,便會(huì)得到錯(cuò)誤的結(jié)果。
另外,由于原碼與補(bǔ)碼之間的轉(zhuǎn)換過程是相同的,原碼按位取反加一可以得到補(bǔ)碼,補(bǔ)碼按位取反加一可以得到原碼,所以二者之間的轉(zhuǎn)換不需要額外的硬件電路。
int main(){ int a = 1;//00000000 00000000 00000000 00000001 int b = 1;//00000000 00000000 00000000 00000001 int c = a - b;//a + (-b) 1 + -1 //11111111 11111111 11111111 11111111 - -1的補(bǔ)碼 //00000000 00000000 00000000 00000001 - 1的補(bǔ)碼 //00000000 00000000 00000000 00000000 - 相加結(jié)果 return 0;}
如果你在X86平臺(tái)上有過內(nèi)存調(diào)試經(jīng)驗(yàn),就一定會(huì)發(fā)現(xiàn)數(shù)據(jù)并沒有乖乖地按順序排列起來(lái),而是一個(gè)倒序排列。這與機(jī)器的內(nèi)存存儲(chǔ)模式有關(guān)。
對(duì)于大小大于一個(gè)字節(jié)的數(shù)據(jù),必然會(huì)存在如何安排多個(gè)字節(jié)的問題,這就產(chǎn)生了大端存儲(chǔ)模式和小端存儲(chǔ)模式。
當(dāng)前存在兩種存儲(chǔ)模式:大端存儲(chǔ)和小端存儲(chǔ)。大端存儲(chǔ)又稱為大端字節(jié)序存儲(chǔ),這種模式在存儲(chǔ)數(shù)據(jù)時(shí),將數(shù)據(jù)的低位存在高地址處,將數(shù)據(jù)的高位存在低地址處;小端存儲(chǔ)模式在存儲(chǔ)數(shù)據(jù)時(shí)恰好相反,體現(xiàn)出數(shù)據(jù)呈倒序存儲(chǔ)的現(xiàn)象。不管是大端或小端,兩者都是以字節(jié)為單位進(jìn)行存儲(chǔ)的。
浮點(diǎn)型數(shù)據(jù)雖然種類少,但是存儲(chǔ)方式相對(duì)復(fù)雜。
int main(){ int n = 9; float *pFloat = (float*)&n; printf("n的值為:%d\n",n); printf("*pFloat的值為:%f\n",*pFloat); *pFloat = 9.0; printf("num的值為:%d\n",n); printf("*pFloat的值為:%f\n",*pFloat); return 0;}
程序的輸出情況是什么?如果你運(yùn)行程序,大概率會(huì)發(fā)現(xiàn)程序的輸出并不如你所料。為了弄清楚這個(gè)現(xiàn)象,就需要了解浮點(diǎn)型的存儲(chǔ)規(guī)則。
根據(jù) IEEE 754標(biāo)準(zhǔn)規(guī)定,任意一個(gè)二進(jìn)制浮點(diǎn)數(shù) ??F?
? 都可以寫成以下形式:
?(-1)^S?
?表示符號(hào)位,??S = 0?
? 時(shí)表示正數(shù),??S = 1?
?時(shí)表示負(fù)數(shù)??M?
? 是有效數(shù)字,M >= 1 && M < 2??2^E?
? 表示指數(shù)位舉例:
5.5在內(nèi)存中的存儲(chǔ):5.5轉(zhuǎn)化為二進(jìn)制:5.0 = 101.1101.1轉(zhuǎn)化為標(biāo)準(zhǔn)形式:101.1 = (-1)^0 * 1.011 * 2^2所以5.5在內(nèi)存中存儲(chǔ)時(shí):S = 0; M = 1.011; E = 2
IEEE 754標(biāo)準(zhǔn)規(guī)定:
32位的浮點(diǎn)數(shù)在內(nèi)存中以這種規(guī)則存儲(chǔ):最高的 1位是符號(hào)位??S?
?,接著是8位的指數(shù)??E?
?,最后的 23位為有效位。
64位的浮點(diǎn)數(shù)在內(nèi)存中以這種規(guī)則存儲(chǔ):最高的 1位是符號(hào)位??S?
?,接著是 11位的指數(shù)??E?
?,最后的 52位為有效位。
由于有效位總是大等于一小于二的,所以在存儲(chǔ)時(shí)只存儲(chǔ)小數(shù)點(diǎn)后面的數(shù)字,這樣可以節(jié)省一位有效數(shù)字。
對(duì)于指數(shù) E 有些特別的規(guī)定:
E為一個(gè)無(wú)符號(hào)整數(shù)。如果E為 8位,則它的取值范圍是??0~255?
?;如果E為11位,則它的取值范圍是??0~2047?
?。但實(shí)際上,E 可能出現(xiàn)為負(fù)數(shù)的情況,為了解決這個(gè)問題,需要在存入內(nèi)存時(shí)將E加上一個(gè)中間值(127或1023)。比如存儲(chǔ)5.5時(shí),E為2,在存儲(chǔ)時(shí)需要加上127,即在內(nèi)存中存儲(chǔ)的是129,即??1000 0001?
?。
將E從內(nèi)存中取出時(shí)有三種情況:
一.E即含 0 又含 1
這種情況直接將E減去相應(yīng)的中間值即得到原值。
二.E為全0
這時(shí)E為 1減去中間值即為真實(shí)值(-126或-1022),且 M不再加上一,而是還原為??0.xxxxxx?
?以表示接近0的數(shù)字。
三.E為全1
這時(shí)可以將E的原值看做128或1024,結(jié)合S的值,表示正/負(fù)無(wú)窮大。
這時(shí)我們可以對(duì)開始的例子作出解釋。
int main(){ int n = 9; //00000000 00000000 00000000 00001001 - 9的補(bǔ)碼 float *pFloat = (float*)&n; //0 00000000 00000000000000000001001 - 在pFloat的視角下9的存儲(chǔ)模式是浮點(diǎn)型 //S E M //(-1)^0 * 0.00000000000000000001001 * 2^(-126) //以浮點(diǎn)型打印即為:0.000000 printf("n的值為:%d\n",n);//9 printf("*pFloat的值為:%f\n",*pFloat);//0.000000 *pFloat = 9.0;//對(duì)n進(jìn)行修改 //1001.0 - 9.0 //(-1)^0 * 1.001 * 2^3 //0 10000010 00100000000000000000000 - 9.0 //01000001000100000000000000000000 - 以%d形式打印的n printf("num的值為:%d\n",n);//1,091,567,616 printf("*pFloat的值為:%f\n",*pFloat);//9.0 return 0;}
標(biāo)簽: 數(shù)據(jù)類型 存儲(chǔ)數(shù)據(jù) 指針類型