C語(yǔ)言詳細(xì)分析不同類型數(shù)據(jù)在內(nèi)存中的存儲(chǔ)
數(shù)據(jù)類型的介紹
在我們之前的學(xué)習(xí)當(dāng)中我們已經(jīng)介紹了基本的內(nèi)置類型
char 字符數(shù)據(jù)類型
short 短整型
int 整形
long 長(zhǎng)整型
long long 更長(zhǎng)的整形
float 單精度浮點(diǎn)數(shù)
double 雙精度浮點(diǎn)數(shù)
這些類型的意義是:
1.使用這個(gè)類型開(kāi)辟內(nèi)存空間的大小,大小決定了使用范圍
2.如何看待內(nèi)存空間的視角。
類型的基本歸類
整形
整形中分為有符號(hào)整形和無(wú)符號(hào)整形,因?yàn)槲覀兩笾杏行?shù)值需要有正數(shù)和負(fù)數(shù)之分,例如溫度我們就可以使用有符號(hào)整形,但是有些不需要負(fù)數(shù)的數(shù)值例如身高,我們就可以使用無(wú)符號(hào)整形。
char
unsigned char
signed char
short
unsigned short [int]
signed short [int]
int
unsigned int
signed int
long
unsigned long [int]
signed long [int]
浮點(diǎn)型
float
double
構(gòu)造類型
數(shù)組類型
結(jié)構(gòu)體類型
struct
枚舉類型
enum
聯(lián)合類型
union
指針類型
int pi;
char pc;
float pf;
void pv;
空類型
void表示空類型(無(wú)類型)
通常應(yīng)用于函數(shù)的返回類型,函數(shù)的參數(shù)、指針類型
整形在內(nèi)存中的存儲(chǔ)
我們都知道,創(chuàng)建一個(gè)變量需要在內(nèi)存中開(kāi)辟空間,那變量究竟是怎么在內(nèi)存當(dāng)中存儲(chǔ)的呢。我們又要提到原碼、反碼、補(bǔ)碼的概念了。
在計(jì)算機(jī)中整數(shù)有三種表示方式即原碼、反碼、補(bǔ)碼,他們都有符號(hào)位和數(shù)值位,符號(hào)位用0表示正數(shù),用1表示負(fù)數(shù)。
正整數(shù)的原碼、反碼、補(bǔ)碼相同,都是直接將屬豬按照正負(fù)數(shù)的形式翻譯成二進(jìn)制形式就可以得到原碼。
負(fù)整數(shù)的原碼、反碼、補(bǔ)碼
原碼
直接將數(shù)值按照正負(fù)數(shù)的形式翻譯成二進(jìn)制形式就可以得到原碼
反碼
將原碼的符號(hào)位不變,其他位一次按位取反就可以得到反碼
補(bǔ)碼
反碼+1就是補(bǔ)碼
對(duì)于一個(gè)整形,數(shù)據(jù)在內(nèi)存當(dāng)中存放的其實(shí)是他的補(bǔ)碼。那么為什么是補(bǔ)碼而是不是原碼呢,原因是使用補(bǔ)碼,可以將符號(hào)位和數(shù)值域統(tǒng)一處理,同時(shí)加法和減法也可以統(tǒng)一處理,我們的cpu只有加法器,此外,補(bǔ)碼與原碼相互轉(zhuǎn)換,其運(yùn)算過(guò)程是相同的,不需要額外的硬件電路。
例如:
1 - 1
因?yàn)閏pu中只有加法器,所以我們需要將減法轉(zhuǎn)化為加法,1+ (-1)如果內(nèi)存中存儲(chǔ)的是原碼,那么他們相加的結(jié)果為
00000000 00000000 00000000 00000001 1的原碼
10000000 00000000 00000000 00000001 -1的原碼
10000000 00000000 00000000 00000010 相加為-2
我們運(yùn)算的答案是錯(cuò)誤的,但是當(dāng)我們存儲(chǔ)的是補(bǔ)碼時(shí):
00000000 00000000 00000000 00000001 1的補(bǔ)碼
11111111 11111111 11111111 11111111 -1的補(bǔ)碼
00000000 00000000 00000000 00000000 相加0
使用補(bǔ)碼運(yùn)算時(shí),得到正確的答案
int main() { int a = 20; int b = -10; return 0; }
如上圖編譯器為了方便顯示的是16進(jìn)制數(shù),其實(shí)對(duì)于一個(gè)整形,數(shù)據(jù)在內(nèi)存當(dāng)中存放的還是他的補(bǔ)碼,我們可以是嘗試還原一下。
例如**-10**
原碼:10000000 00000000 00000000 00001010
反碼:11111111 11111111 11111111 11110101
補(bǔ)碼:11111111 11111111 11111111 11110110
將他的補(bǔ)碼轉(zhuǎn)換成16進(jìn)制就變成了ff ff ff f6,跟我們通過(guò)調(diào)試看到的內(nèi)容一樣
但是我們通過(guò)對(duì)比發(fā)現(xiàn)雖然存儲(chǔ)的是補(bǔ)碼,但是存儲(chǔ)的順序好像不一樣,這是怎么回事呢?
大小端介紹
當(dāng)我們的數(shù)據(jù)大于一個(gè)字節(jié)時(shí),數(shù)據(jù)的存儲(chǔ)就有了順序問(wèn)題。例如:一個(gè)十六進(jìn)制數(shù)0x11223344,我們說(shuō)11為數(shù)據(jù)的高位,44為數(shù)據(jù)的低位。
大端存儲(chǔ)模式:是指數(shù)據(jù)的低位保存在高地址中,而數(shù)據(jù)的高位,保存在內(nèi)存的低地址處。
小端存儲(chǔ)模式:是指數(shù)據(jù)的低位保存在低地址中,而數(shù)據(jù)的高位,保存在內(nèi)存的高地址處。
int main() { int a = 0x11223344; return 0; }
我們通過(guò)調(diào)試得出,在vs2019中為小端存儲(chǔ)模式,因?yàn)閿?shù)據(jù)的低位44存儲(chǔ)在地址處,高位11存儲(chǔ)在高地址處。
一道筆試題
我們通過(guò)一道筆試題來(lái)鞏固一下我們剛才所總結(jié)的知識(shí)。題目:請(qǐng)簡(jiǎn)述大端字節(jié)序和小端字節(jié)序的概念,設(shè)計(jì)一個(gè)小程序來(lái)判斷當(dāng)前機(jī)器的字節(jié)序。
小端字節(jié)序存儲(chǔ)
把數(shù)據(jù)的低位存儲(chǔ)在內(nèi)存的低地址處,高位字節(jié)的內(nèi)容,存儲(chǔ)在內(nèi)存的高地址處 大端字節(jié)序存儲(chǔ) 把數(shù)據(jù)的低字節(jié)的內(nèi)容,存放到內(nèi)存的高地址處,高字節(jié)內(nèi)容,存放在低地址處
int check_sys() { int a = 1; return *(char*)&a; } int main() { if (check_sys() == 1) { printf("小端存儲(chǔ)\n"); } else { printf("大端存儲(chǔ)\n"); } return 0; }
浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)
首先我們通過(guò)一段代碼來(lái)觀察浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)
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; }
以我們的認(rèn)識(shí),使用%d
打印應(yīng)該打印9使用%f
打印應(yīng)該打印9.0,但事實(shí)是這樣的嘛
我們將代碼跑起來(lái)發(fā)現(xiàn),并非我們所認(rèn)識(shí)的,那這說(shuō)明了什么,說(shuō)明了浮點(diǎn)型與整形在內(nèi)存當(dāng)中的存儲(chǔ)規(guī)則是不一樣的,那我們先了解一下浮點(diǎn)數(shù)究竟怎么儲(chǔ)存的。
浮點(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ù)位。
例如:十進(jìn)制5.0,寫成二進(jìn)制是101.0,相當(dāng)于1.0122
所以s = 0,m = 1.01, e = 2。
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ī)定:
前面說(shuō)過(guò), 1≤M<2 ,也就是說(shuō),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è)無(wú)符號(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。比如,2^10的E是10,所以保存成32位浮點(diǎn)數(shù)時(shí),必須保存成10+127=137,即10001001。
既然我們可以將浮點(diǎn)數(shù)存儲(chǔ),我們就可以將他們?nèi)〕鰜?lái),但是指數(shù)E從內(nèi)存中取出分為三種情況:
E不全為0或不全為1
這時(shí),浮點(diǎn)數(shù)就采用下面的規(guī)則表示,即指數(shù)E的計(jì)算值減去127(或1023),得到真實(shí)值,再將 有效數(shù)字M前加上第一位的1。
E全為0
這時(shí),浮點(diǎn)數(shù)的指數(shù)E等于1-127(或者1-1023)即為真實(shí)值,有效數(shù)字M不再加上第一位的1,而是還原為0.xxxxxx的小數(shù)。這樣做是為了表示±0,以及接近于0的很小的數(shù)字。
E全為1
這時(shí),如果有效數(shù)字M全為0,表示±無(wú)窮大(正負(fù)取決于符號(hào)位s)
剖析題目
這些就是浮點(diǎn)數(shù)在內(nèi)存當(dāng)中的存儲(chǔ)規(guī)則,了解了這些后讓我們回到最開(kāi)始的那道題,我們進(jìn)行分析。
首先我們創(chuàng)建了一個(gè)整形變量,那么他在內(nèi)存中存儲(chǔ)的是什么呢?是他的補(bǔ)碼00000000000000000000000000001001,當(dāng)我們將他看作一個(gè)浮點(diǎn)數(shù)打印時(shí),我們可以得到S = 0,M = 000 0000 0000 0000 0000 1001,E = 00000000。所以經(jīng)過(guò)計(jì)算(-1)^ 0 × 0.00000000000000000001001×2 ^ (-126) = 1.001×2 ^ (-146), 所以根據(jù)規(guī)則他是一個(gè)非常接近0的數(shù),使用浮點(diǎn)數(shù)打印就是0.000000
筆試題的第二部分,我們將一個(gè)浮點(diǎn)型使用整形打印,同樣的道理,我們先將9.0用浮點(diǎn)數(shù)的方式存進(jìn)內(nèi)存當(dāng)中,9.0寫成二進(jìn)制為1001.0,可以寫成(-1)^0 * 1.001*2^3,我們可以推出二進(jìn)制形式:0 10000010 001 0000 0000 0000 0000 0000,所以將他作為一個(gè)整形打印時(shí)就會(huì)是一個(gè)非常大的數(shù)為1091567616
到此這篇關(guān)于C語(yǔ)言詳細(xì)分析不同類型數(shù)據(jù)在內(nèi)存中的存儲(chǔ)的文章就介紹到這了,更多相關(guān)C語(yǔ)言數(shù)據(jù)存儲(chǔ)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言計(jì)算Robots機(jī)器人行走路線
這篇文章介紹了C語(yǔ)言計(jì)算Robots機(jī)器人行走路線,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12C語(yǔ)言遞歸之漢諾塔和青蛙跳臺(tái)階問(wèn)題
這篇文章主要介紹了C語(yǔ)言遞歸之漢諾塔問(wèn)題和青蛙跳臺(tái)階問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04C++?Cartographer源碼中關(guān)于傳感器的數(shù)據(jù)傳遞實(shí)現(xiàn)
這篇文章主要介紹了C++?Cartographer源碼中關(guān)于傳感器的數(shù)據(jù)傳遞實(shí)現(xiàn),前面已經(jīng)談到了Cartographer中添加軌跡的方法和傳感器的數(shù)據(jù)流動(dòng)走向。發(fā)現(xiàn)在此調(diào)用了LaunchSubscribers這個(gè)函數(shù)來(lái)訂閱相關(guān)傳感器數(shù)據(jù)2023-03-03Ubuntu 20.04 下安裝配置 VScode 的 C/C++ 開(kāi)發(fā)環(huán)境(圖文教程)
這篇文章主要介紹了Ubuntu 20.04 下安裝配置 VScode 的 C/C++ 開(kāi)發(fā)環(huán)境,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05C++算術(shù)運(yùn)算符與類型轉(zhuǎn)換
這篇文章主要介紹了C++算術(shù)運(yùn)算符與類型轉(zhuǎn)換,C++當(dāng)中提供5種基礎(chǔ)的算術(shù)運(yùn)算符,分別是加法、減法、乘法、除法和取模。下main我們就一起來(lái)看看下面文章得具體舉例與說(shuō)明,需要的朋友可以參考一下,希望對(duì)你有所幫助2021-11-11C++小知識(shí):不要節(jié)約代碼行數(shù)
今天小編就為大家分享一篇關(guān)于C++小知識(shí):不要節(jié)約代碼行數(shù),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01C語(yǔ)言實(shí)現(xiàn)strlen的三種方法小結(jié)
本文主要介紹了C語(yǔ)言實(shí)現(xiàn)strlen的三種方法小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06C++在成員函數(shù)中使用STL的find_if函數(shù)實(shí)例
這篇文章主要介紹了C++在成員函數(shù)中使用STL的find_if函數(shù)實(shí)例,包括了STL中find_if函數(shù)的具體用法及相關(guān)的完整實(shí)例,非常具有參考借鑒價(jià)值,需要的朋友可以參考下2014-10-10