C語(yǔ)言中數(shù)據(jù)如何存儲(chǔ)進(jìn)內(nèi)存揭秘
內(nèi)存簡(jiǎn)單介紹
大家肯定經(jīng)常聽(tīng)說(shuō)內(nèi)存這個(gè)詞,內(nèi)存到底是什么呢? 在計(jì)算機(jī)中,進(jìn)程都要加載進(jìn)內(nèi)存中,也是我們各種數(shù)據(jù)的流通途徑,C語(yǔ)言中,大家肯定都知道指針變量,指針變量中保存的就是內(nèi)存的地址,那么,什么是內(nèi)存的地址呢?
內(nèi)存的單位是字節(jié)
對(duì)于32位的機(jī)器,有32根地址線,每根地址線在尋址時(shí),產(chǎn)生的高低電壓分別為0/1,那么32根地址線產(chǎn)生的地址就會(huì)是
00000000000000000000000000000000
00000000000000000000000000000001
00000000000000000000000000000010
…
11111111111111111111111111111111
這里就有2^32次方個(gè)地址
大家應(yīng)該知道,還有64位的機(jī)器,64根地址線又有多少個(gè)地址呢,大家可以計(jì)算一下
在32位的機(jī)器上,地址是32個(gè)0或者1組成二進(jìn)制序列,那地址就得用4個(gè)字節(jié)的空間來(lái)存儲(chǔ),
所以一個(gè)指針變量的大小就應(yīng)該是4個(gè)字節(jié)。 那如果在64位機(jī)器上,有64個(gè)地址線,那一個(gè)指針變量的大小是8個(gè)字節(jié),才能存放一個(gè)地址。
這串編號(hào)就是內(nèi)存單元的地址,就像酒店的房間號(hào)一樣,對(duì)應(yīng)著內(nèi)存中的一個(gè)字節(jié)大小的房間
我們?cè)趘s中來(lái)看一下
這里是以十六進(jìn)制的方式展示的,大家也知道,32個(gè)數(shù)字看起來(lái)太長(zhǎng)了。
整數(shù)與字符在內(nèi)存中的存儲(chǔ)
關(guān)于c語(yǔ)言中的數(shù)據(jù)類型,大家在寫了這么多代碼后肯定也很清楚了,C語(yǔ)言中有整型、浮點(diǎn)型、字符型、等等
我們來(lái)研究一下整數(shù)在內(nèi)存中是如何存儲(chǔ)的
大家都知道,定義變量,會(huì)在內(nèi)存中開(kāi)辟空間來(lái)存儲(chǔ)
int a = 20;
int類型在vs中占據(jù)4個(gè)字節(jié)的空間,那么如何存儲(chǔ)呢?
這就涉及到原碼反碼補(bǔ)碼的概念
- 計(jì)算機(jī)中的整數(shù)有三種2進(jìn)制表示方法,即原碼、反碼和補(bǔ)碼。
- 三種表示方法均有符號(hào)位和數(shù)值位兩部分,符號(hào)位都是用0表示“正”,用1表示“負(fù)”,剩下數(shù)值位
- 正數(shù)的原、反、補(bǔ)碼都相同。
- 負(fù)整數(shù)的三種表示方法各不相同。
原碼:
直接將數(shù)值按照正負(fù)數(shù)的形式翻譯成二進(jìn)制就可以得到原碼
反碼:
將原碼的符號(hào)位不變,其他位依次按位取反就可以得到反碼
補(bǔ)碼:
反碼+1就得到補(bǔ)碼。
對(duì)于整型變量來(lái)說(shuō),內(nèi)存中存放的其實(shí)是補(bǔ)碼
使用補(bǔ)碼,可以將符號(hào)位和數(shù)值域統(tǒng)一處理,加法和減法也可以同一處理,因?yàn)镃PU只有加法器
eg:
int a = 20; //原碼 : 直接寫二進(jìn)制 00000000000000000000000000010100 //反碼--補(bǔ)碼 -- 正數(shù)的原反補(bǔ)相同 int b = -5; //原碼: 10000000000000000000000000000101 //反碼:符號(hào)位不變,按位取反 // 11111111111111111111111111111010 //補(bǔ)碼:反碼+1 // 11111111111111111111111111111011 -- -5的補(bǔ)碼
上邊可以看見(jiàn)-5的補(bǔ)碼是11111111111111111111111111111011 ,我們?nèi)绾未_認(rèn)呢?
轉(zhuǎn)換成16進(jìn)制為fffffffb
大家可以看到確實(shí)是使用補(bǔ)碼存儲(chǔ)的,但是為什么是倒著存儲(chǔ)的,后邊再來(lái)說(shuō),這是由于大小端的問(wèn)題
給大家舉幾個(gè)例子,不知道存儲(chǔ)無(wú)法分析出結(jié)果代碼
//1. #include <stdio.h> int main() { char a = -128; printf("%u\n",a); //-128 原碼: 10000000 00000000 00000000 10000000 //-128 反碼: 11111111 11111111 11111111 01111111 //-128 補(bǔ)碼: 11111111 11111111 11111111 10000000 //存在a里面的,因?yàn)橹挥幸粋€(gè)字節(jié) 10000000 //所以會(huì)當(dāng)做無(wú)符號(hào)整數(shù)打印 --整形提升 //提升為 11111111111111111111111110000000 //所以結(jié)果是一個(gè)很大的整數(shù),大家可以嘗試一下 return 0; }
#include <stdio.h> int main() { char a = 128; //128的原碼反碼補(bǔ)碼 : 00000000000000000000000010000000 //存在a里的:10000000 //整形提升 :11111111111111111111111110000000 //所以結(jié)果還是那個(gè)很大的整數(shù) printf("%u\n",a); return 0;
例子就簡(jiǎn)單給大家舉到這里,大家一定要記住整數(shù)在內(nèi)存中是以補(bǔ)碼的形式存儲(chǔ)的
字符存儲(chǔ)的是ASCII碼,所以和整數(shù)同
浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)
我們來(lái)看一下浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)
拋磚引玉:
#include <stdio.h> 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; }
上面代碼的打印結(jié)果到底是什么呢?
是不是非常出乎大家的意料呢,這里就可以看出,浮點(diǎn)數(shù)的存儲(chǔ)肯定和整數(shù)是不同的。那浮點(diǎn)數(shù)到底咋存的呢?
根據(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ù)位。
看得很迷糊,直接上例子
v = 5.5
= 101.1 --二進(jìn)制表示
= 1.011 * 2 ^2 – 科學(xué)記數(shù)法表示
因?yàn)槭钦龜?shù) s = 0
m = 1.011
e = 2
IEEE 754規(guī)定:
對(duì)于32位的浮點(diǎn)數(shù),最高的1位是符號(hào)位s,接著的8位是指數(shù)E,剩下的23位為有效數(shù)字M。
IEEE 754對(duì)有效數(shù)字M和指數(shù)E,還有一些特別規(guī)定
1=<M <2 ,所以M可以寫成1.xxxxx 所以可以舍去1 ,只存儲(chǔ)xxxxxx
IEEE 754規(guī)定,在計(jì)算機(jī)內(nèi)部保存M時(shí),默認(rèn)這個(gè)數(shù)的第一位總是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的時(shí)候,只保存01,等到讀取的時(shí)候,再把第一位的1加上去。這樣做的目的,是節(jié)省1位有效數(shù)字。以32位浮點(diǎn)數(shù)為例,留給M只有23位,將第一位的1舍去以后,等于可以保存24位有效數(shù)字
對(duì)于指數(shù)E,E是一個(gè)無(wú)符號(hào)整數(shù),但是科學(xué)記數(shù)法指數(shù)是可以出現(xiàn)負(fù)數(shù)的,所以
IEEE 754規(guī)定了偏移量,如果E為8位,則加上127 ,如果E為11位,則加上1023
我們舉個(gè)例子
float f = 8.5f;
//二進(jìn)制 : 1001.1
//科學(xué)計(jì)數(shù)法表示: 1.0011*2^3
//S = 0 M = 1.0011 E = 3
//存儲(chǔ)進(jìn)去的應(yīng)該是:
0 10000010 00110000000000000000000
//我們可以驗(yàn)證一下
轉(zhuǎn)換成16進(jìn)制
0100 0001 0001 1000 0000 0000 0000 0000
//41 18 00 00
我們來(lái)看一下代碼
和我們想的一樣
我們?cè)賮?lái)舉一個(gè)負(fù)數(shù)的例子
float t = -3.5f;
//二進(jìn)制: 11.1
//科學(xué)記數(shù)法: 1.11*2^1
//S = 1 M = 1.11 E = 1
//存儲(chǔ):
1 10000000 11000000000000000000000
//轉(zhuǎn)換成16進(jìn)制
1100 0000 0011 0000 0000 0000 0000 0000
c0 60 00 00
那我們從內(nèi)存中讀取出來(lái)的二進(jìn)制位如何解析成浮點(diǎn)數(shù)呢
關(guān)于E,有三種情況
E不全為0或不全為1
這時(shí),浮點(diǎn)數(shù)就采用下面的規(guī)則表示,即指數(shù)E的計(jì)算值減去127(或1023),得到真實(shí)值,再將有效數(shù)字M前加上第一位的1
2.E全為0
這時(shí),浮點(diǎn)數(shù)的指數(shù)E等于1-127(或者1-1023)即為真實(shí)值,
有效數(shù)字M不再加上第一位的1,而是還原為0.xxxxxx的小數(shù)。這樣做是為了表示±0,以及接近于 0的很小的數(shù)字。
3.E全為1
這時(shí),如果有效數(shù)字M全為0,表示±無(wú)窮大(正負(fù)取決于符號(hào)位s);
我們來(lái)分析一下最開(kāi)始的題目
浮點(diǎn)數(shù)和整數(shù)的存儲(chǔ)就介紹到這里了,有哪里不清楚的朋友可以私信我
大小端存儲(chǔ)模式及簡(jiǎn)介
上邊有一個(gè)懸念,為什么是倒序存儲(chǔ)的
那什么是大端小端呢?
大端(存儲(chǔ))模式: 數(shù)據(jù)的低位存儲(chǔ)在內(nèi)存的高地址中,數(shù)據(jù)的高位存儲(chǔ)在內(nèi)存的低地址中
小端(存儲(chǔ))模式: 數(shù)據(jù)的低位存儲(chǔ)在內(nèi)存的低地址中,數(shù)據(jù)的高位存儲(chǔ)在內(nèi)存的高地址中
那為什么會(huì)有大小端呢?
內(nèi)存中以字節(jié)為單位,但是比如int 是4個(gè)字節(jié),那如何安排這個(gè)4個(gè)字節(jié)呢?就導(dǎo)致了大小端存儲(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)選擇是大端模式 還是小端模式。
那我們?nèi)绾螠y(cè)試當(dāng)前電腦是哪種存儲(chǔ)模式呢?
int main(void) { int a = 0x11223344; return 0; }
我們調(diào)試打開(kāi)內(nèi)存看一下
很明顯,數(shù)據(jù)的低位存儲(chǔ)在內(nèi)存低地址中,所以為小端存儲(chǔ)模式。
我們能不能寫一個(gè)程序,直接告訴我們大小端呢?
我們來(lái)分析一下
我們來(lái)看一下代碼
int decide_byte_orde() { int i = 1; return *(char *)&i; }
我們來(lái)測(cè)試一下
總結(jié)
深入理解數(shù)據(jù)的存儲(chǔ)是非常有必要的,我們之后碰到很多問(wèn)題都會(huì)豁然開(kāi)朗,大家一定要好好研究一下
到此這篇關(guān)于C語(yǔ)言中數(shù)據(jù)如何存儲(chǔ)揭秘的文章就介紹到這了,更多相關(guān)C語(yǔ)言數(shù)據(jù)存儲(chǔ)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于C++語(yǔ)言實(shí)現(xiàn)機(jī)動(dòng)車違章處罰管理系統(tǒng)
這篇文章主要介紹了基于C++語(yǔ)言實(shí)現(xiàn)機(jī)動(dòng)車違章處罰管理系統(tǒng)的相關(guān)資料,需要的朋友可以參考下2016-07-07C++編程產(chǎn)生指定范圍內(nèi)的隨機(jī)數(shù)
這篇文章主要為大家詳細(xì)介紹了C++編程產(chǎn)生指定范圍內(nèi)的隨機(jī)數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09C++ 中"priority_queue" 優(yōu)先級(jí)隊(duì)列實(shí)例詳解
這篇文章主要介紹了C++ 中"priority_queue" 優(yōu)先級(jí)隊(duì)列實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04如何通過(guò)C++在Bing搜索引擎上進(jìn)行命令行搜索
這篇文章主要介紹了通過(guò)C++在Bing搜索引擎上進(jìn)行命令行搜索,在這篇文章中,我們將介紹一個(gè)簡(jiǎn)單的C++程序,允許用戶通過(guò)命令行輸入搜索詞,在Bing搜索引擎上執(zhí)行搜索,并在默認(rèn)瀏覽器中顯示搜索結(jié)果,需要的朋友可以參考下2023-12-12