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