深度剖析数据在内存中的存储(修炼内功~吊打面试官)
🌈前言
🌟
(???(??? )😉 我是Scort
快上車,握好方向盤跟我有一起打天下嘞!
想送給大家一句話:
🔥我們的選擇決定我們成為怎樣的人,而我們永遠(yuǎn)有權(quán)利去做 對(duì)的選擇。
🌍博客主頁(yè):張小姐的貓~江湖背景🌍
數(shù)據(jù)在內(nèi)存中的存儲(chǔ)
- 🌈前言
- 一、🌟數(shù)據(jù)類型介紹
- 🌽類型的基本歸類
- 🍋整型家族
- 🍋浮點(diǎn)型家族
- 🍋構(gòu)造類型
- 🍋指針類型
- 🍋空類型
- 二、🌽整數(shù)在內(nèi)存中的存儲(chǔ)
- 🍋原碼,反碼,補(bǔ)碼
- 🍋大小端介紹
- 🍋小練習(xí)
- 三、🌽 浮點(diǎn)型在內(nèi)存中的存儲(chǔ)
- 🍋舉個(gè)例子
- 🍋浮點(diǎn)數(shù)存儲(chǔ)規(guī)則
- 🤔突發(fā)奇想
- 🏆
一、🌟數(shù)據(jù)類型介紹
前面我們已經(jīng)學(xué)習(xí)了基本的內(nèi)置類型:
char //字符數(shù)據(jù)類型
short //短整型
int //整形
long //長(zhǎng)整型
long long //更長(zhǎng)的整形
float //單精度浮點(diǎn)數(shù)
double //雙精度浮點(diǎn)型
注意: C語(yǔ)言中沒有字符串類型!
🌽類型的基本歸類
🍋整型家族
char:
unsigned char
signed char
short:
unsigned short [int]
signed short [int]
int:
unsigned int
signed int
long:
unsigned long [int]
signed long [int]
其中char雖然是字符類型,但是字符類型存儲(chǔ)的時(shí)候,存儲(chǔ)的字符的ascii碼是整數(shù)
char c1; signed char c2; unsigned char c3;敲黑板:1、C語(yǔ)言不確定char是有符號(hào)還是無符號(hào)的?是取決于編譯器的實(shí)現(xiàn)
2、C語(yǔ)言中規(guī)定int等價(jià)于signed int、short等價(jià)于signed short
這里我們來解釋一下什么是有符號(hào),無符號(hào)↓
🍋浮點(diǎn)型家族
float
double
long double
🍋構(gòu)造類型
構(gòu)造類型(自定義類型)
數(shù)組類型
結(jié)構(gòu)體類型 struct
枚舉類型 enum
聯(lián)合類型 union
int arr[10]中 arr的類型是int [10],同理char arr3[4]的類型是char [4]
其中int a[]={0,0,0}的類型是int [3]
🍋指針類型
int *pi
char *pc
float *pf
void *pv
🍋空類型
void 表示空類型(無類型)通常應(yīng)用于函數(shù)的返回類型、函數(shù)的參數(shù)、指針類型。
void 則為“無類型指針”,void 可以指向任何類型的數(shù)據(jù)。
注意!!!:
1、如果函數(shù)沒有返回值,那么應(yīng)聲明為void類型
2、如果函數(shù)無參數(shù),那么應(yīng)聲明其參數(shù)為void。
3、小心使用void指針類型,不能對(duì)void指針進(jìn)行算法操作
4、如果函數(shù)的參數(shù)可以是任意類型指針,那么應(yīng)聲明其參數(shù)為void *
5、void不能代表一個(gè)真實(shí)的變量
若想更加詳細(xì)了解void用法可以閱讀C/C++中void用法總結(jié)進(jìn)行更深入的研究。
二、🌽整數(shù)在內(nèi)存中的存儲(chǔ)
我們知道一個(gè)變量的創(chuàng)建是要在內(nèi)存中開辟空間的??臻g的大小是根據(jù)不同的類型決定的
那接下來我們談?wù)剶?shù)據(jù)在所開辟內(nèi)存中到底是如何存儲(chǔ)的?
int a = 10; int b = 20;我們知道為a分配四個(gè)字節(jié)的空間,那如何存儲(chǔ)呢?接下來了解一下下面的概念↓
🍋原碼,反碼,補(bǔ)碼
計(jì)算機(jī)中的整數(shù)有三種表示方法,即原碼、反碼和補(bǔ)碼。
三種方法均有符號(hào)位和數(shù)值位兩部分,符號(hào)位 0 表示“正”, 用1 表示“負(fù)”。
負(fù)整數(shù)的三種表示方法各不相同。
原碼:直接將二進(jìn)制按照正負(fù)數(shù)的形式翻譯成二進(jìn)制就可以了。
反碼:將原碼的符號(hào)位不變,其他位依次按位取反就可以得到了。
補(bǔ)碼:反碼 +1 就得到補(bǔ)碼。
正數(shù)的原碼、反碼、補(bǔ)碼相同。
對(duì)整形來說:數(shù)據(jù)存放內(nèi)存中其實(shí)存放的是補(bǔ)碼。
為什么呢?
1、使用補(bǔ)碼可以將符號(hào)位和數(shù)值域統(tǒng)一處理
2、加法和減法可以統(tǒng)一處理(CPU只有加法器)
3、補(bǔ)碼和原碼互相轉(zhuǎn)化,其運(yùn)算過程是相同的,不需要額外的電路。
舉個(gè)例子:
1 - 1;1+(-1) CPU只有加法器使用原碼計(jì)算000000000000000000000000000000011000000000000000000000000000000110000000000000000000000000000010 算出來是-2使用補(bǔ)碼的二進(jìn)制計(jì)算00000000000000000000000000000001 (1)11111111111111111111111111111111 (-1)補(bǔ)碼100000000000000000000000000000000 只保留32位00000000000000000000000000000000 所以為010000000000000000000000000000001 (-1)原碼11111111111111111111111111111110 (-1)反碼11111111111111111111111111111111 (-1)補(bǔ)碼由此可見用原碼計(jì)算1-1并不為零,而用補(bǔ)碼可計(jì)算出正確值。
接下來我們看看在內(nèi)存中的存儲(chǔ):
我們可以看見對(duì)于a和b分別存儲(chǔ)的是補(bǔ)碼。 可是有沒有覺得不對(duì)勁嗎?為什么數(shù)字是反過來排列的?難道不是應(yīng)該是 00 00 00 14和 ff ff ff f6 嗎?
這里就要引入小端存儲(chǔ)和大端存儲(chǔ)。
🍋大小端介紹
什么是大端小端?
大端字節(jié)序存儲(chǔ):是指數(shù)據(jù)的低位保存在內(nèi)存的高地址中,而數(shù)據(jù)的高位,保存在內(nèi)存的低地址中;
小端字節(jié)序存儲(chǔ):是指數(shù)據(jù)的低位保存在內(nèi)存的低地址中,而數(shù)據(jù)的高位,保存在內(nèi)存的高地址中;
對(duì)于16進(jìn)制0x11223344來說,44是最低位的字節(jié)數(shù)據(jù),33是次低位。
但為什么會(huì)有大端小端呢?
這是因?yàn)樵谟?jì)算機(jī)系統(tǒng)中,我們是以字節(jié)為單位的,每個(gè)地址單元都對(duì)應(yīng)著一個(gè)字節(jié),一個(gè)字節(jié)為8 bit。
但是在C語(yǔ)言中除了8 bit的char之外,還有16 bit的short型,32 bit的long型(要看具體的編
譯器),另外,對(duì)于位數(shù)大于8位的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個(gè)字節(jié),那么必然存在著一個(gè)如何將多個(gè)字節(jié)安排的問題。因此就導(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處理器還可以由硬件來選擇是大端模式還是小端模式
答:因?yàn)楸厝淮嬖谥粋€(gè)如何將多個(gè)字節(jié)安排。
🍋小練習(xí)
百度2015年系統(tǒng)工程師筆試題:
請(qǐng)簡(jiǎn)述大端字節(jié)序和小端字節(jié)序的概念,設(shè)計(jì)一個(gè)小程序來判斷當(dāng)前機(jī)器的字節(jié)序。
🥃解題思路:通過char*來訪問1的首字節(jié)判斷是否為1
📜代碼實(shí)現(xiàn)
int main() {int a = 1;char*p=(char*)&a;//要對(duì)&a進(jìn)行類型轉(zhuǎn)化if (*p == 1){printf("小端\n");}else {printf("大端\n");}return 0; }三、🌽 浮點(diǎn)型在內(nèi)存中的存儲(chǔ)
常見的浮點(diǎn)數(shù):
3.14159
1E10
浮點(diǎn)數(shù)家族包括: float、double、long double 類型。
浮點(diǎn)數(shù)表示的范圍:float.h中定義
🍋舉個(gè)例子
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é)果的什么呢?
num 和 *pFloat 在內(nèi)存中明明是同一個(gè)數(shù),為什么浮點(diǎn)數(shù)和整數(shù)的解讀結(jié)果會(huì)差別這么大?跟我來吧🌋
🍋浮點(diǎn)數(shù)存儲(chǔ)規(guī)則
根據(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è)例子:↓
十進(jìn)制的5.5,轉(zhuǎn)化成二進(jìn)制就變成了101.1,用科學(xué)計(jì)數(shù)法表示就是 1.011 * 2^2 。那么,按照上面的格式,可以得出S = 0, M = 1.011,E = 2 。
同理十進(jìn)制的9.0,轉(zhuǎn)化成二進(jìn)制就變成了1001.0,用科學(xué)計(jì)數(shù)法表示就是 1.001 * 2^3 。那么,按照上面的格式,可以得出S = 0, M = 1.001,E = 3 。
IEEE 754規(guī)定:
對(duì)于32位的浮點(diǎn)數(shù),最高的1位是符號(hào)位s,接著的8位是指數(shù)E,剩下的23位為有效數(shù)字M。
對(duì)于64位的浮點(diǎn)數(shù),最高的1位是符號(hào)位S,接著的11位是指數(shù)E,剩下的52位為有效數(shù)字M。
IEEE 754對(duì)有效數(shù)字M和指數(shù)E,還有一些特別規(guī)定:
1≤M<2 ,也就是說,M可以寫成 1.xxxxxx 的形式,其中xxxxxx表示小數(shù)部分。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ù)字。
至于指數(shù)E,情況就比較復(fù)雜。
首先,E為一個(gè)無符號(hào)整數(shù)(unsigned int)
這意味著,如果E為8位,它的取值范圍為0 ~ 255;如果E為11位,它的取值范圍為0 ~ 2047。但是,我們知道,科學(xué)計(jì)數(shù)法中的E是可以出現(xiàn)負(fù)數(shù)的,所以IEEE 754規(guī)定,存入內(nèi)存時(shí)E的真實(shí)值必須再加上一個(gè)中間數(shù)(修正值),對(duì)于8位的E,這個(gè)中間數(shù)是127;對(duì)于11位的E,這個(gè)中間數(shù)是1023 。這個(gè)是IEEE 754 的死規(guī)定(只能死記啦)
比如,2^10的E是10,所以保存成32位浮點(diǎn)數(shù)時(shí),必須保存成10+127=137,即10001001。
😈舉個(gè)例子:
發(fā)現(xiàn)在內(nèi)存中的地址果然是0x40b00000
指數(shù)E從內(nèi)存中取出還可以再分成三種情況:
1??E不全為0或不全為1 →(怎么樣放怎么樣拿出來)
這時(shí),浮點(diǎn)數(shù)就采用下面的規(guī)則表示,即指數(shù)E的計(jì)算值減去127(或1023),得到真實(shí)值,再將有效數(shù)字M前加上第一位的1。
比如:
0.5(1/2)的二進(jìn)制形式為0.1,由于規(guī)定正數(shù)部分必須為1,即將小數(shù)點(diǎn)右移1位,則為
1.0*2^(-1),其階碼為-1+127=126,表示為01111110,而尾數(shù)1.0去掉整數(shù)部分為0,補(bǔ)齊0到23位00000000000000000000000,則其二進(jìn)
制表示形式為:
0 01111110 00000000000000000000000
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,表示±無窮大(正負(fù)取決于符號(hào)位s);
好了,關(guān)于浮點(diǎn)數(shù)的表示規(guī)則,就說到這里。
解釋前面的題目:
🤔突發(fā)奇想
既然浮點(diǎn)數(shù)的儲(chǔ)存不能精確存儲(chǔ),那數(shù)據(jù)會(huì)不會(huì)不準(zhǔn)確呢🤔
因?yàn)楦↑c(diǎn)數(shù)存儲(chǔ)會(huì)有誤差,所以不能直接相比較,應(yīng)該比兩者之間的差值是否在允許的范圍內(nèi)
float f = 0.00001;if (f - 0.0){}return 0;如果同學(xué)們還想了解更多細(xì)膩的知識(shí),可以看比特蛋哥~深入解刨的視頻
🏆
💯如有錯(cuò)誤可以指出💯 🥇想學(xué)嗎?我教你啊🥇 🎉🎉覺得博主寫的還不錯(cuò)的可以一鍵三連撒🎉🎉總結(jié)
以上是生活随笔為你收集整理的深度剖析数据在内存中的存储(修炼内功~吊打面试官)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用R筛选数据
- 下一篇: Codeforces Global Ro