網(wǎng)站首頁 編程語言 正文
本篇主要是深度剖析整型與浮點(diǎn)型數(shù)據(jù)在內(nèi)存中是如何儲存與讀取的。弄明白數(shù)據(jù)在內(nèi)存中的存儲方式有助于我們更好的理解代碼。
1.整型在內(nèi)存中的存儲
1.1有符號整型
在研究整型變量的存儲之前,我們要知道,整型有以下幾種類型
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可以省略,但是為什么會有char這個類型呢?,我們知道char類型叫做字符型,它表示的是用來存儲一個字符的類型,把它作為整型是因?yàn)樽址趦?nèi)存中是以ASCII值儲存的,所以我們把char型也歸為整型。每一個類型又細(xì)分出signed和unsigned兩個類型,叫做有符號型和無符號型,在我們不寫有沒有符號時,short,int,long這三個類型默認(rèn)是有符號類型,只有char類型在不寫時是有符號類型還是無符號類型是不確定的,取決于編譯器,我使用的是vs2022,這個編譯器下char默認(rèn)為signed char。
當(dāng)我們在代碼中創(chuàng)建一個變量時,我們知道要在內(nèi)存上開辟一塊空間來儲存這個變量,那么整型變量在內(nèi)存中到底是怎么存儲的呢?我們用代碼來看一下。
我們創(chuàng)建了a與b兩個整型變量,然后把3賦給a,-1賦給b,然后我們再在內(nèi)存中尋找a與b,如上圖,靠左邊的第一個行就是b的地址與b內(nèi)存儲的數(shù)值,靠右邊的第一行就是a的地址與a中存儲的元素,vs在展示內(nèi)存時,為了方便展示,顯示的是十六進(jìn)制的數(shù)據(jù),而數(shù)據(jù)在內(nèi)存中是以二進(jìn)制存儲的,一個字節(jié)的大小是八個比特位,八個比特位有2^8種數(shù)=16^2,剛好可以用兩位十六進(jìn)制數(shù)表示,所以在上圖中存放數(shù)據(jù)的位置每兩位表示一個字節(jié),因?yàn)槲覀兇娣诺氖钦驼妓膫€字節(jié),所以我把內(nèi)存顯示的寬度調(diào)為四列,方便我們觀察。
我們知道,計算機(jī)中的有符號數(shù)有三種表示方法:原碼,反碼,補(bǔ)碼。
這三種方法都有符號位和數(shù)值位,他們二進(jìn)制位的第一位就是符號位,0表示正數(shù),1表示負(fù)數(shù),其他位就是數(shù)值位。那么這三種表示方法都是怎么得到的呢?
我們直接給出結(jié)論:正數(shù)的原碼,反碼,補(bǔ)碼相同,負(fù)數(shù)的原,反,補(bǔ)碼由以下規(guī)則計算
原碼:直接把數(shù)轉(zhuǎn)換成二進(jìn)制序列
反碼:原碼的二進(jìn)制序列符號位不變,數(shù)值位按位取反
補(bǔ)碼:反碼的二進(jìn)制序列加一
現(xiàn)在我們知道了這三種表示形式都是怎么得到的,那么計算機(jī)在存儲時到底使用的是哪一種呢?我們來計算一下
首先,我們來算3
因?yàn)?是正數(shù),那么他的原,反,補(bǔ)碼都是相同的,為
(為了方便觀察,我會在后面每次展示二進(jìn)制序列時每隔八位打一個空格)
00000000 00000000 00000000 00000011--3(二進(jìn)制)
0 0 0 0 0 0 0 3 --3(十六進(jìn)制)
我們再來看-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)過一個簡單的計算我們可以發(fā)現(xiàn),我們內(nèi)存中存儲的其實(shí)是補(bǔ)碼,那么為什么內(nèi)存存儲的是補(bǔ)碼呢?
因?yàn)镃PU只有加法器,當(dāng)我們想要計算比如1-1時,計算機(jī)會模擬成1-(-1)這時候我們?nèi)绻迷a來計算,那么結(jié)果是這樣的
00000000 00000000 00000000 00000001--(1)
10000000 00000000 00000000 00000001--(-1)
10000000 00000000 00000000 00000010--(1+(-1))==(-2)?
我們發(fā)現(xiàn)這個值等于-2,是不正確的,如果我們使用補(bǔ)碼來計算
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ǔ)碼計算的結(jié)果是正確的,使用補(bǔ)碼還有一個好處
11111111 11111111 11111111 11111111--(-1)二進(jìn)制補(bǔ)碼
10000000 00000000 00000000 00000000--對(-1)的二進(jìn)制補(bǔ)碼按位取反
10000000 00000000 00000000 00000001--又得到了(-1)的原碼
即我們可以使用相同的方法完成原碼與補(bǔ)碼之間的轉(zhuǎn)換。
使用補(bǔ)碼可以將符號位和數(shù)值位統(tǒng)一處理,同時,加法與減法也可以統(tǒng)一處理。
1.2無符號整型
以上我們就明白了整型數(shù)是怎么在內(nèi)存中存儲的,但是注意,我在定義a與b時使用的是int,我們說每個整形都有兩個細(xì)分類型,分別是有符號整型與無符號整型,在我們前面沒有寫的時候默認(rèn)為有符號整型,那么無符號整型是怎么存儲的呢?
因?yàn)閏har類型比較小,所以我們以一個char類型為例
如果這是一個有符號的char,第一位是符號位
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)
如果這是一個無符號char,八個都是數(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)
同理,其他整型的有符號型和無符號型也是可以這樣計算
1.3大小端字節(jié)序
我們知道了整型在內(nèi)存中的存儲方法后,現(xiàn)在在回到最開始的代碼,我們發(fā)現(xiàn)在存儲3時我們存儲的順序是03 00 00 00,這是為什么呢?難到是反著存的嗎?
?這與大小端字節(jié)序有關(guān),那么什么是大小端字節(jié)序呢,我們往下看
比如我現(xiàn)在要存放一個十六進(jìn)制數(shù)11 22 33 44,在內(nèi)存中現(xiàn)在有這么兩種方法
(1)
11 22 33 44
低地址 ---> 高地址
(2)
44 33 22 11
低地址 ---> 高地址
我們把方法(1)叫做大端字節(jié)序存儲,即:把一個數(shù)的低位字節(jié)序(低位)內(nèi)容放在高地址處,高位字節(jié)序放在低地址處。
把方法(2)叫做小段字節(jié)序存儲,即:把一個數(shù)的高位字節(jié)序(高位)內(nèi)容放在高地址處,低位字節(jié)序放在低地址處。
我們現(xiàn)在看一下當(dāng)前編譯器是采用了什么存儲模式
?我們可以看到,地址是由低到高的,而存儲順序也是由低位到高位的,所以是小段字節(jié)序存儲。
為什么會有大小端模式之分呢?這是因?yàn)樵谟嬎銠C(jī)系統(tǒng)中,我們是以字節(jié)為單位的,每個地址單元 都對應(yīng)著一個字節(jié),一個字節(jié)為8bit。但是在C語言中除了8 bit的char之外,還有16 bit的short型,32 bit的long型(要看具體的編 譯器),另外,對于位數(shù)大于8位 的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個字節(jié),那么必然存在著一個如何將多個字節(jié)安排的問題。因此就 導(dǎo)致了大端存儲模式和小端存儲模式。 例如:一個 16bit 的 short 型 x ,在內(nèi)存中的地址為 0x0010 , x 的值為 0x1122 ,那么 0x11 為 高字節(jié), 0x22 為低字節(jié)。對于大端 模式,就將 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式, 剛好相反。我們常用的 X86 結(jié)構(gòu)是 小端模式,而 KEIL C51 則為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以 由硬件來選擇是大端模式還是小端 模式。
2. 浮點(diǎn)數(shù)在內(nèi)存中的儲存
我們先來看這樣一段代碼
?
?我們現(xiàn)在來分析一下,為什么是這樣的情況呢?
要搞明白這個結(jié)果,我們就要先弄清楚浮點(diǎn)數(shù)在計算機(jī)內(nèi)部的表示方法。
根據(jù)國際標(biāo)準(zhǔn)IEEE(電氣和電子工程協(xié)會) 754,任意一個二進(jìn)制浮點(diǎn)數(shù)V可以表示成下面的形式:
? (-1)^S * M * 2^E (-1)^s表示符號位,當(dāng)s=0,V為正數(shù);當(dāng)s=1,V為負(fù)數(shù)。
? M表示有效數(shù)字,大于等于1,小于2。
? 2^E表示指數(shù)位。
?這是什么意思呢?,我們打個比方,比如我們現(xiàn)在要表示浮點(diǎn)數(shù)6.5
6.5 --(十進(jìn)制)
110.1 --(二進(jìn)制)
1.101*2^2 --(科學(xué)計數(shù)法)
(-1)^0*1.101*2^2 --(IEEE754)
按照IEEE754的規(guī)定,S=0,M=1.101,E=2。
那么在內(nèi)存中我們是怎么來存儲這些數(shù)的呢,我們以單精度浮點(diǎn)型為例
float占4個字節(jié),32個比特位,我們?nèi)绾蝸矸峙溥@32個bit位呢?
我們以一個*代表一個bit位,把32位的內(nèi)存這樣劃分:
* ******** ***********************
S(1) E(8) M(23)
IEEE754規(guī)定:第一個比特位用來存符號位,接著八位存指數(shù)E,剩下的23位存有效數(shù)M
64位的double類型
* *********** ****************************************************
S(1) E(11) M(52)
對與64位的double型,最高位是符號位S,接著十一位是指數(shù)E,剩下的52位存有效數(shù)M
在規(guī)定中M是大于1小于2的數(shù),那么我們可以知道M總是1.xxxxxxxx。也就是說M的第一位永遠(yuǎn)是1,那么我們在存儲是是不是可以不存這一位,只存小數(shù)點(diǎn)后面的位,然后在使用的時候再把這個1加上即可,這樣的話我們的23位存儲M的空間就可以多存儲一位數(shù)了,以此可以提高我們浮點(diǎn)數(shù)的精度。
在存儲指數(shù)E時,首先,我們規(guī)定E是一個無符號數(shù),如果E為8位,它的取值就是0~255,若E為11位,取值就為0~2047,但是當(dāng)我們實(shí)際用來表示一個數(shù)時,(比如小于1的浮點(diǎn)數(shù)),指數(shù)位E就會是負(fù)數(shù),這時候就出現(xiàn)了問題,所以IEEE754規(guī)定,在存儲E時,E的真實(shí)值必須加上一個中間數(shù),8位加127,11位加1023,然后再存入E區(qū)
在讀取指數(shù)E時,又分為3種情況
(1)E不全為0或不全為1(正常情況)
把計算值減去127(或1023)得到真實(shí)值,再在M前面加上省去的那個1即可以還原
(2)E全為0
這時我們直接規(guī)定E的真實(shí)值為1-127或(1-1023),M前面不再加1,還原為0.xxxxx的小數(shù)
我們可以計算一下,E=0-127,這個數(shù)就是(-1)^S*M*2^(-127),可以想象的到這是一個非常小的數(shù),這是為了表示±0以及無限接近0的數(shù)。
(3)E全為1
E+127=255,E=128,我們知道2^32的值為42億多,那么2^128就是4個42億相乘,可以想象這也是一個非常大的值了,所以這時的M全為0,表示±無窮大
知道了以上這些內(nèi)容,我們再回過頭來看那個代碼
?首先是第一個printf,以整型打印n,結(jié)果是9,沒有問題
再來看第二個printf,我們用*pFloat以浮點(diǎn)數(shù)的視角來訪問這四個字節(jié),這時候電腦會認(rèn)為這里存的是浮點(diǎn)數(shù)。
00000000 00000000 00000000 00001001--9
0 00000000 00000000000000000001001--以浮點(diǎn)數(shù)的視角來看
S E M
這時這個二進(jìn)制序列就會以浮點(diǎn)數(shù)的規(guī)則被翻譯,我們發(fā)現(xiàn)它的E全為0,符號位為0,所以表示0.000000。所以打印的第二個值也沒有問題了
我們再來看第三個printf,我們先是以浮點(diǎn)數(shù)的形式在這個地址存放一個9.0,再以整型的方式打印出來。
9.0-->1001.0-->1.001*2^3
E=3+127=130-->10000010
0 10000010 10010000000000000000000--以浮點(diǎn)數(shù)的方式存放9.0
S E M
我們再把這個數(shù)以整型的讀取方式讀出來的值就是1091567616
再看第四個printf,以浮點(diǎn)數(shù)的方式讀取剛剛存貯的9.0,結(jié)果自然也是9.0
以上就是本篇的全部內(nèi)容,如果大家覺得我的文章對你有幫助,希望能夠給我點(diǎn)贊支持一下,鐵柱在這里謝謝大家了!!
原文鏈接:https://blog.csdn.net/qq_45967533/article/details/122860682
相關(guān)推薦
- 2022-04-09 cas5 編譯安裝依賴時提示: Failure to find net.shibboleth.too
- 2022-04-25 基于Matplotlib?調(diào)用?pyplot?模塊中?figure()?函數(shù)處理?figure圖形對
- 2022-08-15 gin框架中使用websocket發(fā)送消息及群聊
- 2022-09-14 C/C++實(shí)現(xiàn)HTTP協(xié)議解析的示例代碼_C 語言
- 2022-05-26 Flutter開發(fā)實(shí)現(xiàn)底部留言板_Android
- 2022-08-07 Go?gRPC教程實(shí)現(xiàn)Simple?RPC_Golang
- 2022-03-03 Module build failed: Error: Node Sass version 6.0.
- 2022-02-04 關(guān)于傳統(tǒng)并行計算框架(比如MPI)和MapReduce并行計算框架比較
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支