日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無(wú)先后,達(dá)者為師

網(wǎng)站首頁(yè) 編程語(yǔ)言 正文

C語(yǔ)言-剖析數(shù)據(jù)是如何在內(nèi)存中存儲(chǔ)的(整型與浮點(diǎn)型)

作者:c鐵柱同學(xué) 更新時(shí)間: 2022-02-12 編程語(yǔ)言

本篇主要是深度剖析整型與浮點(diǎn)型數(shù)據(jù)在內(nèi)存中是如何儲(chǔ)存與讀取的。弄明白數(shù)據(jù)在內(nèi)存中的存儲(chǔ)方式有助于我們更好的理解代碼。

1.整型在內(nèi)存中的存儲(chǔ)

1.1有符號(hào)整型

在研究整型變量的存儲(chǔ)之前,我們要知道,整型有以下幾種類型

char   signed char
       unsigned char
short  signed short [int]
       unsigned short [int]
int    signed int
       unsigned int
long   signed long [int]
       unsigned long [int]

short 與long的全稱是short int 與long int,后面的int可以省略,但是為什么會(huì)有char這個(gè)類型呢?,我們知道char類型叫做字符型,它表示的是用來(lái)存儲(chǔ)一個(gè)字符的類型,把它作為整型是因?yàn)樽址趦?nèi)存中是以ASCII值儲(chǔ)存的,所以我們把char型也歸為整型。每一個(gè)類型又細(xì)分出signed和unsigned兩個(gè)類型,叫做有符號(hào)型和無(wú)符號(hào)型,在我們不寫有沒有符號(hào)時(shí),short,int,long這三個(gè)類型默認(rèn)是有符號(hào)類型,只有char類型在不寫時(shí)是有符號(hào)類型還是無(wú)符號(hào)類型是不確定的,取決于編譯器,我使用的是vs2022,這個(gè)編譯器下char默認(rèn)為signed char。

當(dāng)我們?cè)诖a中創(chuàng)建一個(gè)變量時(shí),我們知道要在內(nèi)存上開辟一塊空間來(lái)儲(chǔ)存這個(gè)變量,那么整型變量在內(nèi)存中到底是怎么存儲(chǔ)的呢?我們用代碼來(lái)看一下。

我們創(chuàng)建了a與b兩個(gè)整型變量,然后把3賦給a,-1賦給b,然后我們?cè)僭趦?nèi)存中尋找a與b,如上圖,靠左邊的第一個(gè)行就是b的地址與b內(nèi)存儲(chǔ)的數(shù)值,靠右邊的第一行就是a的地址與a中存儲(chǔ)的元素,vs在展示內(nèi)存時(shí),為了方便展示,顯示的是十六進(jìn)制的數(shù)據(jù),而數(shù)據(jù)在內(nèi)存中是以二進(jìn)制存儲(chǔ)的,一個(gè)字節(jié)的大小是八個(gè)比特位,八個(gè)比特位有2^8種數(shù)=16^2,剛好可以用兩位十六進(jìn)制數(shù)表示,所以在上圖中存放數(shù)據(jù)的位置每?jī)晌槐硎疽粋€(gè)字節(jié),因?yàn)槲覀兇娣诺氖钦驼妓膫€(gè)字節(jié),所以我把內(nèi)存顯示的寬度調(diào)為四列,方便我們觀察。

我們知道,計(jì)算機(jī)中的有符號(hào)數(shù)有三種表示方法:原碼,反碼,補(bǔ)碼。

這三種方法都有符號(hào)位和數(shù)值位,他們二進(jìn)制位的第一位就是符號(hào)位,0表示正數(shù),1表示負(fù)數(shù),其他位就是數(shù)值位。那么這三種表示方法都是怎么得到的呢?

我們直接給出結(jié)論:正數(shù)的原碼,反碼,補(bǔ)碼相同,負(fù)數(shù)的原,反,補(bǔ)碼由以下規(guī)則計(jì)算

原碼:直接把數(shù)轉(zhuǎn)換成二進(jìn)制序列

反碼:原碼的二進(jìn)制序列符號(hào)位不變,數(shù)值位按位取反

補(bǔ)碼:反碼的二進(jìn)制序列加一

現(xiàn)在我們知道了這三種表示形式都是怎么得到的,那么計(jì)算機(jī)在存儲(chǔ)時(shí)到底使用的是哪一種呢?我們來(lái)計(jì)算一下

首先,我們來(lái)算3

因?yàn)?是正數(shù),那么他的原,反,補(bǔ)碼都是相同的,為

(為了方便觀察,我會(huì)在后面每次展示二進(jìn)制序列時(shí)每隔八位打一個(gè)空格)

00000000 00000000 00000000 00000011--3(二進(jìn)制)
0   0    0   0    0   0    0   3   --3(十六進(jìn)制)

我們?cè)賮?lái)看-1

原碼:10000000 00000000 00000000 00000001-- -1(二進(jìn)制原碼)
反碼:11111111 11111111 11111111 11111110-- -1(二進(jìn)制反碼)
補(bǔ)碼:11111111 11111111 11111111 11111111-- -1(二進(jìn)制補(bǔ)碼)
補(bǔ)碼:f   f    f   f    f   f    f   f   -- -1(十六進(jìn)制補(bǔ)碼)

經(jīng)過(guò)一個(gè)簡(jiǎn)單的計(jì)算我們可以發(fā)現(xiàn),我們內(nèi)存中存儲(chǔ)的其實(shí)是補(bǔ)碼,那么為什么內(nèi)存存儲(chǔ)的是補(bǔ)碼呢?

因?yàn)镃PU只有加法器,當(dāng)我們想要計(jì)算比如1-1時(shí),計(jì)算機(jī)會(huì)模擬成1-(-1)這時(shí)候我們?nèi)绻迷a來(lái)計(jì)算,那么結(jié)果是這樣的

00000000 00000000 00000000 00000001--(1)
10000000 00000000 00000000 00000001--(-1)
10000000 00000000 00000000 00000010--(1+(-1))==(-2)?

我們發(fā)現(xiàn)這個(gè)值等于-2,是不正確的,如果我們使用補(bǔ)碼來(lái)計(jì)算

 10000000 00000000 00000000 00000001--(-1)二進(jìn)制原碼
 11111111 11111111 11111111 11111110--(-1)二進(jìn)制反碼
 11111111 11111111 11111111 11111111--(-1)二進(jìn)制補(bǔ)碼
 00000000 00000000 00000000 00000001--(1)二進(jìn)制補(bǔ)碼
100000000 00000000 00000000 00000000--(1+(-1))的二進(jìn)制補(bǔ)碼
 00000000 00000000 00000000 00000000--(1+(-1))的二進(jìn)制原碼=0

我們發(fā)現(xiàn),使用補(bǔ)碼計(jì)算的結(jié)果是正確的,使用補(bǔ)碼還有一個(gè)好處

11111111 11111111 11111111 11111111--(-1)二進(jìn)制補(bǔ)碼
10000000 00000000 00000000 00000000--對(duì)(-1)的二進(jìn)制補(bǔ)碼按位取反
10000000 00000000 00000000 00000001--又得到了(-1)的原碼

即我們可以使用相同的方法完成原碼與補(bǔ)碼之間的轉(zhuǎn)換。

使用補(bǔ)碼可以將符號(hào)位和數(shù)值位統(tǒng)一處理,同時(shí),加法與減法也可以統(tǒng)一處理。

1.2無(wú)符號(hào)整型

以上我們就明白了整型數(shù)是怎么在內(nèi)存中存儲(chǔ)的,但是注意,我在定義a與b時(shí)使用的是int,我們說(shuō)每個(gè)整形都有兩個(gè)細(xì)分類型,分別是有符號(hào)整型與無(wú)符號(hào)整型,在我們前面沒有寫的時(shí)候默認(rèn)為有符號(hào)整型,那么無(wú)符號(hào)整型是怎么存儲(chǔ)的呢?

因?yàn)閏har類型比較小,所以我們以一個(gè)char類型為例

如果這是一個(gè)有符號(hào)的char,第一位是符號(hào)位
00000000-->0
00000001-->1
00000010-->2
00000011-->3
  …………
01111111-->2^7-1-->127
10000000--不進(jìn)行運(yùn)算,直接翻譯為-128
10000001-->-(2^7-1)-->-127
10000010-->-126
  …………
11111110-->-2
11111111-->-1

signed char的范圍為(-128~127)

如果這是一個(gè)無(wú)符號(hào)char,八個(gè)都是數(shù)值位
00000000-->0
00000001-->1
00000010-->2
00000011-->3
  …………
01111111-->2^7-1-->127
10000000-->128
10000001-->129
10000010-->130
  …………
11111110-->254
11111111-->2^8-1-->255

unsigned char的范圍為(0~255)

同理,其他整型的有符號(hào)型和無(wú)符號(hào)型也是可以這樣計(jì)算

1.3大小端字節(jié)序

我們知道了整型在內(nèi)存中的存儲(chǔ)方法后,現(xiàn)在在回到最開始的代碼,我們發(fā)現(xiàn)在存儲(chǔ)3時(shí)我們存儲(chǔ)的順序是03 00 00 00,這是為什么呢?難到是反著存的嗎?

?這與大小端字節(jié)序有關(guān),那么什么是大小端字節(jié)序呢,我們往下看

比如我現(xiàn)在要存放一個(gè)十六進(jìn)制數(shù)11 22 33 44,在內(nèi)存中現(xiàn)在有這么兩種方法

(1)

     11 22 33 44
低地址   --->    高地址   

(2)

     44 33 22 11
低地址   --->    高地址   

我們把方法(1)叫做大端字節(jié)序存儲(chǔ),即:把一個(gè)數(shù)的低位字節(jié)序(低位)內(nèi)容放在高地址處,高位字節(jié)序放在低地址處。

把方法(2)叫做小段字節(jié)序存儲(chǔ),即:把一個(gè)數(shù)的高位字節(jié)序(高位)內(nèi)容放在高地址處,低位字節(jié)序放在低地址處。

我們現(xiàn)在看一下當(dāng)前編譯器是采用了什么存儲(chǔ)模式

?我們可以看到,地址是由低到高的,而存儲(chǔ)順序也是由低位到高位的,所以是小段字節(jié)序存儲(chǔ)。

為什么會(huì)有大小端模式之分呢?這是因?yàn)樵谟?jì)算機(jī)系統(tǒng)中,我們是以字節(jié)為單位的,每個(gè)地址單元 都對(duì)應(yīng)著一個(gè)字節(jié),一個(gè)字節(jié)為8bit。但是在C語(yǔ)言中除了8 bit的char之外,還有16 bit的short型,32 bit的long型(要看具體的編 譯器),另外,對(duì)于位數(shù)大于8位 的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個(gè)字節(jié),那么必然存在著一個(gè)如何將多個(gè)字節(jié)安排的問(wèn)題。因此就 導(dǎo)致了大端存儲(chǔ)模式和小端存儲(chǔ)模式。 例如:一個(gè) 16bit 的 short 型 x ,在內(nèi)存中的地址為 0x0010 , x 的值為 0x1122 ,那么 0x11 為 高字節(jié), 0x22 為低字節(jié)。對(duì)于大端 模式,就將 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式, 剛好相反。我們常用的 X86 結(jié)構(gòu)是 小端模式,而 KEIL C51 則為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以 由硬件來(lái)選擇是大端模式還是小端 模式。

2. 浮點(diǎn)數(shù)在內(nèi)存中的儲(chǔ)存

我們先來(lái)看這樣一段代碼

?

?我們現(xiàn)在來(lái)分析一下,為什么是這樣的情況呢?

要搞明白這個(gè)結(jié)果,我們就要先弄清楚浮點(diǎn)數(shù)在計(jì)算機(jī)內(nèi)部的表示方法。

根據(jù)國(guó)際標(biāo)準(zhǔn)IEEE(電氣和電子工程協(xié)會(huì)) 754,任意一個(gè)二進(jìn)制浮點(diǎn)數(shù)V可以表示成下面的形式:

? (-1)^S * M * 2^E (-1)^s表示符號(hào)位,當(dāng)s=0,V為正數(shù);當(dāng)s=1,V為負(fù)數(shù)。

? M表示有效數(shù)字,大于等于1,小于2。

? 2^E表示指數(shù)位。

?這是什么意思呢?,我們打個(gè)比方,比如我們現(xiàn)在要表示浮點(diǎn)數(shù)6.5

6.5               --(十進(jìn)制)
110.1             --(二進(jìn)制)
1.101*2^2         --(科學(xué)計(jì)數(shù)法)
(-1)^0*1.101*2^2  --(IEEE754)

按照IEEE754的規(guī)定,S=0,M=1.101,E=2。

那么在內(nèi)存中我們是怎么來(lái)存儲(chǔ)這些數(shù)的呢,我們以單精度浮點(diǎn)型為例

float占4個(gè)字節(jié),32個(gè)比特位,我們?nèi)绾蝸?lái)分配這32個(gè)bit位呢?

我們以一個(gè)*代表一個(gè)bit位,把32位的內(nèi)存這樣劃分:
 *  ********  ***********************
S(1)  E(8)             M(23)

IEEE754規(guī)定:第一個(gè)比特位用來(lái)存符號(hào)位,接著八位存指數(shù)E,剩下的23位存有效數(shù)M

64位的double類型
 *  ***********  ****************************************************
S(1)   E(11)                           M(52)

對(duì)與64位的double型,最高位是符號(hào)位S,接著十一位是指數(shù)E,剩下的52位存有效數(shù)M

在規(guī)定中M是大于1小于2的數(shù),那么我們可以知道M總是1.xxxxxxxx。也就是說(shuō)M的第一位永遠(yuǎn)是1,那么我們?cè)诖鎯?chǔ)是是不是可以不存這一位,只存小數(shù)點(diǎn)后面的位,然后在使用的時(shí)候再把這個(gè)1加上即可,這樣的話我們的23位存儲(chǔ)M的空間就可以多存儲(chǔ)一位數(shù)了,以此可以提高我們浮點(diǎn)數(shù)的精度。

在存儲(chǔ)指數(shù)E時(shí),首先,我們規(guī)定E是一個(gè)無(wú)符號(hào)數(shù),如果E為8位,它的取值就是0~255,若E為11位,取值就為0~2047,但是當(dāng)我們實(shí)際用來(lái)表示一個(gè)數(shù)時(shí),(比如小于1的浮點(diǎn)數(shù)),指數(shù)位E就會(huì)是負(fù)數(shù),這時(shí)候就出現(xiàn)了問(wèn)題,所以IEEE754規(guī)定,在存儲(chǔ)E時(shí),E的真實(shí)值必須加上一個(gè)中間數(shù),8位加127,11位加1023,然后再存入E區(qū)

在讀取指數(shù)E時(shí),又分為3種情況

(1)E不全為0或不全為1(正常情況)

把計(jì)算值減去127(或1023)得到真實(shí)值,再在M前面加上省去的那個(gè)1即可以還原

(2)E全為0

這時(shí)我們直接規(guī)定E的真實(shí)值為1-127或(1-1023),M前面不再加1,還原為0.xxxxx的小數(shù)

我們可以計(jì)算一下,E=0-127,這個(gè)數(shù)就是(-1)^S*M*2^(-127),可以想象的到這是一個(gè)非常小的數(shù),這是為了表示±0以及無(wú)限接近0的數(shù)。

(3)E全為1

E+127=255,E=128,我們知道2^32的值為42億多,那么2^128就是4個(gè)42億相乘,可以想象這也是一個(gè)非常大的值了,所以這時(shí)的M全為0,表示±無(wú)窮大

知道了以上這些內(nèi)容,我們?cè)倩剡^(guò)頭來(lái)看那個(gè)代碼

?首先是第一個(gè)printf,以整型打印n,結(jié)果是9,沒有問(wèn)題

再來(lái)看第二個(gè)printf,我們用*pFloat以浮點(diǎn)數(shù)的視角來(lái)訪問(wèn)這四個(gè)字節(jié),這時(shí)候電腦會(huì)認(rèn)為這里存的是浮點(diǎn)數(shù)。

00000000 00000000 00000000 00001001--9
 0 00000000 00000000000000000001001--以浮點(diǎn)數(shù)的視角來(lái)看
 S    E                M

這時(shí)這個(gè)二進(jìn)制序列就會(huì)以浮點(diǎn)數(shù)的規(guī)則被翻譯,我們發(fā)現(xiàn)它的E全為0,符號(hào)位為0,所以表示0.000000。所以打印的第二個(gè)值也沒有問(wèn)題了

我們?cè)賮?lái)看第三個(gè)printf,我們先是以浮點(diǎn)數(shù)的形式在這個(gè)地址存放一個(gè)9.0,再以整型的方式打印出來(lái)。

9.0-->1001.0-->1.001*2^3
E=3+127=130-->10000010
 0 10000010 10010000000000000000000--以浮點(diǎn)數(shù)的方式存放9.0
 S    E              M

我們?cè)侔堰@個(gè)數(shù)以整型的讀取方式讀出來(lái)的值就是1091567616

再看第四個(gè)printf,以浮點(diǎn)數(shù)的方式讀取剛剛存貯的9.0,結(jié)果自然也是9.0

以上就是本篇的全部?jī)?nèi)容,如果大家覺得我的文章對(duì)你有幫助,希望能夠給我點(diǎn)贊支持一下,鐵柱在這里謝謝大家了!!

原文鏈接:https://blog.csdn.net/qq_45967533/article/details/122860682

欄目分類
最近更新