C語(yǔ)言數(shù)據(jù)的存儲(chǔ)

2023-01-04 15:11:18 來(lái)源:51CTO博客

前言

之前寫過一篇關(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ù)類型

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ù)類型的意義

數(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ù)讀出。

整形的存儲(chǔ)

作為內(nèi)置數(shù)據(jù)類型最龐大的家族,整形的存儲(chǔ)相對(duì)簡(jiǎn)單。整型在內(nèi)存中以補(bǔ)碼存儲(chǔ),正數(shù)的原碼反碼補(bǔ)碼相同,負(fù)數(shù)需要另做計(jì)算。

原碼、反碼、補(bǔ)碼

原碼即十進(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)型的存儲(chǔ)

浮點(diǎn)型數(shù)據(jù)雖然種類少,但是存儲(chǔ)方式相對(duì)復(fù)雜。

一個(gè)實(shí)例

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ī)則。

存儲(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??如果E11位,則它的取值范圍是??0~2047??。但實(shí)際上,E 可能出現(xiàn)為負(fù)數(shù)的情況,為了解決這個(gè)問題,需要在存入內(nèi)存時(shí)將E加上一個(gè)中間值(127或1023)。比如存儲(chǔ)5.5時(shí),E2,在存儲(chǔ)時(shí)需要加上127,即在內(nèi)存中存儲(chǔ)的是129,即??1000 0001??。

將E從內(nèi)存中取出時(shí)有三種情況:

一.E即含 0 又含 1

這種情況直接將E減去相應(yīng)的中間值即得到原值。

二.E為全0

這時(shí)E1減去中間值即為真實(shí)值(-126或-1022),且 M不再加上一,而是還原為??0.xxxxxx??以表示接近0的數(shù)字

三.E為全1

這時(shí)可以將E的原值看做128或1024,結(jié)合S的值,表示正/負(fù)無(wú)窮大。

帶回實(shí)例

這時(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ù) 指針類型

上一篇:RocketMQ 5.0 多語(yǔ)言客戶端的設(shè)計(jì)與實(shí)現(xiàn)
下一篇:百萬(wàn)并發(fā)場(chǎng)景中倒排索引與位圖計(jì)算的實(shí)踐