C語(yǔ)言進(jìn)階數(shù)據(jù)的存儲(chǔ)機(jī)制完整版
數(shù)據(jù)類型
1.基本內(nèi)置類型:byte,int ,char, float, double
2.構(gòu)造數(shù)據(jù)類型:
數(shù)組類型;
結(jié)構(gòu)體類型:struct
共用體(聯(lián)合類型):union
枚舉類型:enum
3.指針類型 :int* p,char* p,float* p,void* p
4.空類型 : void(無(wú)類型),通常用于函數(shù)的返回類型,函數(shù)參數(shù)與指針類型。
構(gòu)造類型又叫自定義類型,在各自參數(shù)或者元素類型發(fā)生變化就會(huì)讓他徹頭徹尾的改變;而基本數(shù)據(jù)類型的特點(diǎn)就是不可以再分解為其他類型,基本類型就是自我說(shuō)明,關(guān)于他們的作用就不一一贅述了。
內(nèi)存窗口
那首先要在調(diào)試欄打開(kāi)內(nèi)存窗口,并搞清楚怎么觀察內(nèi)存,這是必要的工具
1.地址欄
2.內(nèi)容
這些密密麻麻的就是內(nèi)存中的數(shù)據(jù),看到這里你可能就會(huì)疑惑,不是說(shuō)內(nèi)存里存的都是二進(jìn)制數(shù)嗎,這些是什么鬼?是的,沒(méi)有錯(cuò),但是內(nèi)存窗口展示內(nèi)容有限,在有限的范圍內(nèi),他只能選擇以 16 進(jìn)制的形式展示出來(lái),僅僅是展示而已。
3.文本
這個(gè)更是人不人鬼不鬼的其實(shí)是他根據(jù)內(nèi)存的數(shù)據(jù)簡(jiǎn)單的以文本的格式輸出其可能的內(nèi)容,無(wú)價(jià)值簡(jiǎn)直就是意義不明。
整型的存儲(chǔ)
不論我們?cè)趯懘a時(shí)創(chuàng)建了個(gè)什么東西,他不會(huì)居于虛空,存在載體就會(huì)占用內(nèi)存,而空間的大小是根據(jù)我們創(chuàng)建的數(shù)據(jù)的類型而決定的,我們要回到問(wèn)題最本質(zhì)的源頭,在開(kāi)辟的內(nèi)存中到底如何去存儲(chǔ)數(shù)據(jù)?我們不廢話直接創(chuàng)建倆個(gè)變量看看便知
int main() { int a = 5; int b = -5; return 0; }
內(nèi)存窗口打開(kāi)我們可以取地址查找 a,b 的數(shù)據(jù)存儲(chǔ)情況:
這里是不是感覺(jué)很奇怪,二者為何差異這么大?要搞清楚我們就要繼續(xù)深入研究。
原碼,反碼,補(bǔ)碼
說(shuō)整數(shù)的二進(jìn)制有三種表示方法:原碼,反碼,補(bǔ)碼。
整數(shù)分為正數(shù)和負(fù)數(shù),正負(fù)數(shù)的區(qū)別就在于他們二進(jìn)制32位數(shù)的最高位的 0和1代表著符號(hào)位,0為正,1為負(fù),其余才是有效位。
正數(shù)的原反補(bǔ)三碼合一,和他本身是一樣的。但是負(fù)數(shù)就花哨了,負(fù)數(shù)原碼是按照一個(gè)數(shù)的正,負(fù)直接寫出來(lái)的二進(jìn)制就是原碼。反碼在原碼基礎(chǔ)上,除開(kāi)符號(hào)位進(jìn)行取反得到。這里強(qiáng)調(diào)一下,之前講過(guò)一個(gè)操作符:~(按位取反操作符),區(qū)別一下他倆,按位取反操作符是針對(duì)二進(jìn)制數(shù)每一位全部都取反,包括符號(hào)位。補(bǔ)碼則是反碼的基礎(chǔ)上+1得到,比如 -7 這個(gè)數(shù)的原反補(bǔ)分別為:
10000000 00000000 00000000 00000111 (原)
111111111 111111111 111111111 111111000(反)
111111111 111111111 111111111 111111001(補(bǔ))
b 的 -5 就是 00000000 00000000 00000000 00000101以補(bǔ)碼 11111111 11111111 11111111 11111011 每四個(gè)字節(jié)為一位化成16 進(jìn)制就是 0xfffffff3。
補(bǔ)碼的意義
既然內(nèi)存中中存儲(chǔ)的是二進(jìn)制的補(bǔ)碼,我們現(xiàn)在不談現(xiàn)象談本質(zhì),為什么偏偏要是補(bǔ)碼呢?
我們要明白一件事就是計(jì)算機(jī)算減法是相對(duì)不容易的,因?yàn)镃PU里面沒(méi)有減法器,只有加法器,要算 1-1 時(shí)只能算作 1+(-1)。計(jì)算機(jī)用二進(jìn)制去計(jì)算時(shí),我們會(huì)發(fā)現(xiàn),當(dāng)用原碼或者反碼去計(jì)算根本行不通,只有補(bǔ)碼才可以實(shí)現(xiàn)。
由此看來(lái),補(bǔ)碼的地位是絕對(duì)的老大哥,在計(jì)算機(jī)系統(tǒng)中,數(shù)值一律用補(bǔ)碼來(lái)存儲(chǔ),主要原因是:
1.統(tǒng)一了零的編碼
2.將符號(hào)位和其它位統(tǒng)一處理
3.將減法運(yùn)算轉(zhuǎn)變?yōu)榧臃ㄟ\(yùn)算
4.兩個(gè)用補(bǔ)碼表示的數(shù)相加時(shí),如果最高位(符號(hào)位)有進(jìn)位,則進(jìn)位被舍棄
由這里看,加法和減法可以統(tǒng)一起來(lái)處理,此外補(bǔ)碼和原碼相互轉(zhuǎn)換時(shí),其運(yùn)算過(guò)程是相同的,不需要額外的硬件電路。
大小端模式
之前的博客專門講了大小端存儲(chǔ)模式專題,其實(shí)大小端的檢驗(yàn)也可以用今天的知識(shí)來(lái)解決:
# include<stdio.h> int check_s() { int i = 1; return (*(char*)&i); } int main() { int ret = 0; ret = check_s(); if (ret == 1) { printf("小端\n"); } else { printf("大端\n"); } return 0;
其結(jié)果:
不同數(shù)據(jù)類型存儲(chǔ)
int main() { char a = -1; signed char b = -1; unsigned char c = -1; printf("a = %d,b=%d,c=%d",a,b,c); return 0; }
上面這個(gè)代碼乍一看就是三個(gè)-1,仔細(xì)看就會(huì)發(fā)現(xiàn)char類型后面陰差陽(yáng)錯(cuò)跟個(gè) -1,-1是整數(shù)啊,這時(shí)是不是感覺(jué)有一絲凌亂?-1的原碼為1000(30個(gè)0)1,取反+1得到補(bǔ)碼111……111(32個(gè)1),而 char 只能放 8 個(gè)比特位,就意味著會(huì)發(fā)生截?cái)?,,a = 11111111,而我們?cè)趘s環(huán)境里面 signed char和char是一樣的,我們不妨看看運(yùn)行結(jié)果如何:
為什么c會(huì)是 255呢?首先要明白為什么b 會(huì)是 -1,我們 signed char b在截?cái)嗪?,如果?以%d形式進(jìn)行打印,就會(huì)發(fā)生整型提升,有符號(hào)數(shù)的最高位會(huì)被認(rèn)為是符號(hào)位,而整型提升時(shí)會(huì)以整型的原符號(hào)位進(jìn)行提升,因?yàn)槭秦?fù)數(shù)就會(huì)全部補(bǔ)成1,再按照補(bǔ)碼轉(zhuǎn)原碼倒回去會(huì)發(fā)現(xiàn)結(jié)果就是 char a= -1。
同理,c 是無(wú)符號(hào)數(shù),但也會(huì)發(fā)生整型提升,無(wú)符號(hào)數(shù)提升通通補(bǔ) 0 ,按照%d 打印時(shí),內(nèi)存就會(huì)認(rèn)為這是一個(gè)有符號(hào)數(shù),最高位又會(huì)被默認(rèn)為符號(hào)位,最后轉(zhuǎn)換成原碼,就會(huì)得到 255。
接著上面的思路再來(lái)看看這個(gè)代碼:
# include<stdio.h> # include<windows.h> int main() { unsigned int a ; for(a=9;i>=0;i--) { printf("%u ",i); Sleep(1000); return 0; } }
這個(gè)代碼也是一個(gè)眼見(jiàn)不為實(shí)的代碼,表面上是打印9個(gè)數(shù),實(shí)則實(shí)在無(wú)限的死循環(huán),你發(fā)現(xiàn)了嗎?聰明的你如果看到 判斷條件 i>=0 這個(gè)條件,就可能發(fā)現(xiàn)端倪,十有八九會(huì)死循環(huán),因?yàn)椴还苁裁辞闆r都會(huì)恒成立。我們來(lái)看看運(yùn)行效果:
當(dāng)我0進(jìn)入后很自然的變成了-1,而-1會(huì)放到無(wú)符號(hào)整型,補(bǔ)碼是32位全1,而作為無(wú)符號(hào)整型,最高位不再是符號(hào)位,所有位都是有效位,轉(zhuǎn)化為原碼是一個(gè)巨大的數(shù)字,由于滿足循環(huán)判斷條件于是開(kāi)始?xì)g樂(lè)死循環(huán)。我們可以根據(jù)這些實(shí)例總結(jié)出相應(yīng)的經(jīng)驗(yàn)。
浮點(diǎn)數(shù)存儲(chǔ)機(jī)制
浮點(diǎn)數(shù)相對(duì)于整型,他的存儲(chǔ)機(jī)制會(huì)更復(fù)雜。
浮點(diǎn)數(shù)包括float和double,甚至long double,浮點(diǎn)數(shù)的表示范圍在 float.h中定義。
int main() { int n = 9; float* pFloat = (float*)&n; printf("n=%d",n); printf("*pFloat=%f\n",*pFloat); *pFloat = 9.0; printf("n=%d\n",n); printf("*pFloat=%f\n",*pFloat); return 0; }
對(duì)上面這個(gè)代碼,我一開(kāi)始的覺(jué)得打印的是:9,9, 9.0, 9.0,后來(lái)發(fā)現(xiàn)是我格局小了。
如上結(jié)果我們就能明顯看出浮點(diǎn)數(shù)和整型存儲(chǔ)是不一樣的。
國(guó)際標(biāo)準(zhǔn)IEEE(電氣和電子工程協(xié)會(huì))754標(biāo)準(zhǔn),任意一個(gè)二進(jìn)制浮點(diǎn)數(shù)V都可以表示成下面的形式:
V=(-1)^S * M * 2^E
1. (-1)^s 表示符號(hào)位,s為0表示正,s為1表示負(fù)
2. M 表示有效數(shù)字,大于等于1,小于2
3. 2^E 表示指數(shù)位
對(duì)于32位浮點(diǎn)數(shù),最高符號(hào)位s,接著 8位是指數(shù)E,剩下23位為有效數(shù)字M;而64位浮點(diǎn)數(shù),11位是指數(shù) E,剩下的 52 位有效數(shù)字M。
這里的M和E還有特殊的規(guī)定
IEEE 754規(guī)定,在計(jì)算機(jī)內(nèi)部保存M時(shí),默認(rèn)這個(gè)數(shù)的第1位總是1,因此可以被舍去,只保存后面的小數(shù)部分。例如保存1.01的時(shí)候,只保存01,等到讀取的時(shí)候,再把第一位的1加上去。這樣做的目的,是節(jié)省1位有效數(shù)字。以32位浮點(diǎn)數(shù)為例,留給M只有23位,將第一位的1舍去以后,等于可以保存24位有效數(shù)字。
我們會(huì)把指數(shù)位看作是無(wú)符號(hào)整數(shù),取值在0到255,如果是11位的E,取值在0到2047,但是眾所周知,科學(xué)記數(shù)法里面E是可以出現(xiàn)負(fù)數(shù)的,所以還有規(guī)定就是E在存入時(shí)會(huì)先修飾一波,給真實(shí)值加上一個(gè)中間值,對(duì)于 8位與11位的E,這個(gè)中間數(shù)是127和1023,比如 2 的十次方,E就是10,保存成32位浮點(diǎn)數(shù)時(shí),必須變成10+127=137,即10001001。E在全0和全1的情況下代表著接近于0的無(wú)窮小和無(wú)窮大。
我們回到最開(kāi)始的問(wèn)題,9的原反補(bǔ)三碼合一00000000 00000000 00000000 00001001,E為全0時(shí)其真實(shí)值是-126,除去S與E,小數(shù)部分M為0000……01001,而%f只能打印后6位,這就是為什么打印0.000000。接著pFloat為9.0浮點(diǎn)數(shù)進(jìn)入,對(duì)應(yīng)的二進(jìn)制為1001.0,科學(xué)記數(shù)法1.0012^3,S=0,E=3,M=1.001,存入后變成 0 10000010 0010000000000000000000,(S E M),為了方便我們換成16進(jìn)制計(jì)算出是 0x41100000,我以%f取出就是他自己這沒(méi)什么解釋的,困惑的可能是%d的結(jié)果,以%d打印就默認(rèn)為有符號(hào)數(shù),變成補(bǔ)碼轉(zhuǎn)換出來(lái)就是 1091567616。
今天就到這里吧,摸了家人們,更多關(guān)于C語(yǔ)言進(jìn)階數(shù)據(jù)的存儲(chǔ)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- C語(yǔ)言數(shù)據(jù)存儲(chǔ)歸類介紹
- C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之單鏈表存儲(chǔ)詳解
- C語(yǔ)言深入探索數(shù)據(jù)類型的存儲(chǔ)
- C語(yǔ)言中的字符串?dāng)?shù)據(jù)在C中的存儲(chǔ)方式
- C語(yǔ)言數(shù)據(jù)的存儲(chǔ)專項(xiàng)分析
- 深度解析C語(yǔ)言中數(shù)據(jù)的存儲(chǔ)
- C語(yǔ)言詳細(xì)圖解浮點(diǎn)型數(shù)據(jù)的存儲(chǔ)實(shí)現(xiàn)
- C語(yǔ)言深入分析浮點(diǎn)型數(shù)據(jù)存儲(chǔ)
- C語(yǔ)言中數(shù)據(jù)如何存儲(chǔ)進(jìn)內(nèi)存揭秘
相關(guān)文章
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之單鏈表的查找和建立
鏈表是一種物理存儲(chǔ)結(jié)構(gòu)上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過(guò)鏈表中的指針鏈接次序?qū)崿F(xiàn)的。本文將和大家一起聊聊C語(yǔ)言中單鏈表的查找和建立,感興趣的可以學(xué)習(xí)一下2022-09-09講解C++編程中Address-of運(yùn)算符&的作用及用法
這篇文章主要介紹了C++編程中Address-of運(yùn)算符&的作用及用法,是C++入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2016-01-01基于C語(yǔ)言實(shí)現(xiàn)點(diǎn)菜系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了基于C語(yǔ)言實(shí)現(xiàn)點(diǎn)菜系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11C++ 實(shí)現(xiàn)靜態(tài)鏈表的簡(jiǎn)單實(shí)例
這篇文章主要介紹了C++ 實(shí)現(xiàn)靜態(tài)鏈表的簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-06-06C語(yǔ)言實(shí)現(xiàn)獲取內(nèi)存信息并輸出的實(shí)例
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)獲取內(nèi)存信息并輸出的實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03