摘要:在符號(hào)位中,表示正,表示負(fù)。我們知道對(duì)于整型來(lái)說(shuō),內(nèi)存中存放的是該數(shù)的補(bǔ)碼。在計(jì)算機(jī)系統(tǒng)中,數(shù)值一律用補(bǔ)碼來(lái)表示和存儲(chǔ)。表示有效數(shù)字,。規(guī)定對(duì)于位的浮點(diǎn)數(shù),最高的位是
最近博主開(kāi)學(xué)啦!更新節(jié)奏有點(diǎn)跟不上,這里做個(gè)檢討~
UU們開(kāi)學(xué)感覺(jué)怎么樣呢?見(jiàn)到同學(xué)或者舍友有沒(méi)有很開(kāi)心呢?又可以一起愉快地玩???!
但是玩歸玩,學(xué)習(xí)還是正事,話不多說(shuō),開(kāi)始今天的內(nèi)容。
在之前的內(nèi)容中,我們已經(jīng)把C語(yǔ)言的入門(mén)知識(shí)進(jìn)行了一個(gè)全面的講解,并介紹了一些實(shí)用的調(diào)試技巧,以及函數(shù)棧幀的創(chuàng)建和銷毀,可以說(shuō)對(duì)于C語(yǔ)言已經(jīng)算是敲過(guò)開(kāi)門(mén)磚了。
那么今天,我們就要開(kāi)啟深入學(xué)習(xí)C語(yǔ)言的旅程啦!首先解決C語(yǔ)言進(jìn)階第一問(wèn):數(shù)據(jù)在內(nèi)存中是如何存儲(chǔ)的?
當(dāng)然,我們主要探討的是整型和浮點(diǎn)型這兩種類型。
C語(yǔ)言中具以下幾種基本內(nèi)置類型:
這里說(shuō)明一下:
- C語(yǔ)言的基本內(nèi)置類型只的是C語(yǔ)言本身具有的類型,而庫(kù)函數(shù)本身是不屬于C語(yǔ)言的,是獨(dú)立于C語(yǔ)言之外的。
- C語(yǔ)言是用來(lái)決定語(yǔ)言的語(yǔ)法形式的,而庫(kù)函數(shù)是編譯器的產(chǎn)商提供,當(dāng)然庫(kù)函數(shù)的使用是受C語(yǔ)言標(biāo)準(zhǔn)約束的(比如C語(yǔ)言標(biāo)準(zhǔn)規(guī)定了一些庫(kù)函數(shù)的函數(shù)名、參數(shù)類型、返回值類型和函數(shù)功能)。
- 這樣做的好處是雖然不同編譯器實(shí)現(xiàn)函數(shù)的方式不一樣,但是對(duì)于我們來(lái)說(shuō),在不同的編譯器下使用函數(shù)的方式是一樣的。
- 當(dāng)然在有一些編譯器中對(duì)一些庫(kù)函數(shù)的支持提供得不是很好,比如在VS編譯器下使用scanf函數(shù)就可能會(huì)報(bào)錯(cuò),需要使用scanf_s。
由于字符類型在存儲(chǔ)的時(shí)候是按照ASCII碼值存儲(chǔ)的,所以字符類型也屬于整型家族中的一員。
前面我們已經(jīng)學(xué)習(xí)了以上這么多的數(shù)據(jù)類型,并且了解了它們所占內(nèi)存空間的大小。
我們還要明白,我們?yōu)槭裁匆o數(shù)據(jù)分那么多的類型:
- 我們生活中出了的數(shù)除了整數(shù)就是小數(shù),所以為了更好地存儲(chǔ)這兩種類型,數(shù)據(jù)分為整型和浮點(diǎn)型兩大類
- 使用不同的類型時(shí)內(nèi)存開(kāi)辟的空間大小是不一樣的(使用范圍也不一樣)。我們應(yīng)該根據(jù)數(shù)據(jù)的大小選擇合適的類型,如果選大了,則浪費(fèi)空間;選小了,則數(shù)據(jù)無(wú)法完整存儲(chǔ)。
- 不同類型決定了我們看待空間時(shí)應(yīng)該采用哪種視角。即對(duì)于內(nèi)存中不同空間的類型存和取的方式是不一樣的。
- char
unsigned char
signed char- short
unsigned short [int]
signed short [int]- int
unsigned int
signed int- long
unsigned long [int]
signed long [int]
-long long
unsigned long long [int]
signed long long [int]
注意:
如何理解有符號(hào)和無(wú)符號(hào):
以char類型舉例:
這里也說(shuō)明了不同類型決定了我們看待內(nèi)存中的值時(shí)視角也不同。
同時(shí),我們也可以由此推算出一個(gè)類型中最大能放一個(gè)多大的數(shù)字。
還是以char類型舉例:
由此可以得出,有符號(hào)的char中可以存放的數(shù)的范圍是 -128 ~ 127。
相同道理:
無(wú)符號(hào)的char中可以存放的數(shù)的范圍是 0 ~ 255。
同理,我們可以得出short、int 、long等等類型的范圍。
float
double
這兩種類型通常根據(jù)數(shù)據(jù)要求的精度來(lái)選擇:精度高選擇double類型(8個(gè)字節(jié)),精度低選擇float類型(4個(gè)字節(jié))。
一般我們更常選擇float類型,但是記住噢~3.14默認(rèn)是double類型哦!
除了內(nèi)置類型之外,還有構(gòu)造類型,即可以自己創(chuàng)造的類型。
數(shù)組類型
結(jié)構(gòu)體類型 struct
枚舉類型 enum
聯(lián)合類型 union
數(shù)組類型為什么也屬于構(gòu)造類型呢?
我們來(lái)看看數(shù)組的類型:
當(dāng)我們定義的數(shù)組元素的類型和個(gè)數(shù)不同時(shí),數(shù)組的類型也不同,可以通過(guò)sizeof反映出來(lái),所以我們也說(shuō)數(shù)組屬于構(gòu)造類型。
int *pi
char *pc
float *pf
void *pv
void表示空類型(即無(wú)類型)
常用于函數(shù)的返回類型、函數(shù)參數(shù)和指針類型
這里說(shuō)void作為函數(shù)參數(shù)是什么情況呢?
正常我們?cè)谡{(diào)用函數(shù)的時(shí)候,如果這個(gè)函數(shù)不需要傳參,則不寫(xiě)參數(shù)。
但是如果是一個(gè)不需要傳參的函數(shù),而我們又給它傳參了,函數(shù)會(huì)怎樣呢?在有的編譯器中,會(huì)報(bào)錯(cuò),但是有的編譯器則會(huì)接受這種情況。
但是如果我們給參數(shù)加上一個(gè)void。編譯器就會(huì)報(bào)錯(cuò)或者警告,告訴你不能傳參啦!
我們知道,一個(gè)變量創(chuàng)建之后是要在內(nèi)存中開(kāi)辟空間的,那么這些變量在內(nèi)存中到底是怎么存儲(chǔ)的呢?
接下來(lái)我們就來(lái)看看~
首先看看整型在內(nèi)存中是如何存儲(chǔ)的。
我們知道,二進(jìn)制中有符號(hào)的整數(shù)有原碼、反碼、補(bǔ)碼三種表示方式。
這三種表示方式都有符號(hào)位和數(shù)值位兩部分。
- 在符號(hào)位中,0表示“正”,1表示“負(fù)”。
- 在數(shù)值位中,正數(shù)的原、反、補(bǔ)碼相同;負(fù)數(shù)的三種表示方法各不相同:
原碼:直接將二進(jìn)制按照正負(fù)數(shù)的形式翻譯成二進(jìn)制。
反碼:將原碼的符號(hào)位不變,其他位依次按位取反。
補(bǔ)碼:反碼+1得到補(bǔ)碼。
我們知道對(duì)于整型來(lái)說(shuō),內(nèi)存中存放的是該數(shù)的補(bǔ)碼。
在計(jì)算機(jī)系統(tǒng)中,數(shù)值一律用補(bǔ)碼來(lái)表示和存儲(chǔ)。
但是存放的是補(bǔ)碼,而不是原碼或者反碼呢?
因?yàn)椋褂醚a(bǔ)碼可以將符號(hào)位和數(shù)值位作統(tǒng)一處理。
同時(shí),加法和減法也可以統(tǒng)一處理(CPU只有加法器)。
因此,補(bǔ)碼與原碼相互轉(zhuǎn)換,其運(yùn)算過(guò)程是相同的,不需要額外的硬件電路。
比如,我們想計(jì)算1-1。但是因?yàn)镃UP只有加法運(yùn)算,所以我們可以把表達(dá)式寫(xiě)成1+(-1)。
我們可以看到,用原碼計(jì)算,得不出正確的結(jié)果。
但是如果用補(bǔ)碼計(jì)算,情況就不一樣了。
從上圖的計(jì)算中,我們可以看出,使用補(bǔ)碼可以直接對(duì)符號(hào)位和數(shù)值位同一進(jìn)行處理。
這里有一個(gè)有意思的事情:
我們知道原碼、反碼和補(bǔ)碼都是怎么得到的,那么計(jì)算一下我們會(huì)發(fā)現(xiàn)用同樣的方法,我們也可以通過(guò)補(bǔ)碼得到原碼。
(不信你試著算一下?。?/p>
所以,由此我們可以看到用補(bǔ)碼來(lái)存儲(chǔ)的好處。
我們看到,a是一個(gè)十六進(jìn)制的數(shù),當(dāng)我們?cè)趦?nèi)存中查看它的存儲(chǔ)情況時(shí),可以發(fā)現(xiàn),它的存放是從低地址開(kāi)始放44 33 22 11。
對(duì)于它們?cè)趦?nèi)存中的存放順序,其實(shí)可以有很多種存放的方式。
但是如果大家都按照自己的想法來(lái)寫(xiě),則整個(gè)存儲(chǔ)就會(huì)亂套,而且如果我們不按照正常的順序存儲(chǔ)時(shí),取出來(lái)要獲得原來(lái)的數(shù)就比較麻煩,所以我們最后決定只以下兩種存儲(chǔ)順序。
注意:這里的存儲(chǔ)順序是以字節(jié)為單位來(lái)討論的。因此,也稱字節(jié)序。
其中,上面的存儲(chǔ)順序稱為小端字節(jié)序;下面的存儲(chǔ)順序稱為大端字節(jié)序。
- 小端字節(jié)序存儲(chǔ)
把一個(gè)數(shù)的低位字節(jié)的內(nèi)容,存儲(chǔ)在內(nèi)存的低地址出,把這個(gè)數(shù)的高位字節(jié)的內(nèi)容,存儲(chǔ)在內(nèi)存的高地址處。- 大端字節(jié)序存儲(chǔ)
把一個(gè)數(shù)的低位字節(jié)的內(nèi)容,存儲(chǔ)在內(nèi)存的高地址出,把這個(gè)數(shù)的高位字節(jié)的內(nèi)容,存儲(chǔ)在內(nèi)存的低地址處。
那么,為什么在存儲(chǔ)時(shí)還要有大小端之分呢?
這是因?yàn)樵谟?jì)算機(jī)系統(tǒng)中,內(nèi)存空間的最小單元是一個(gè)字節(jié)。所以,如果我們存放的變量大小大于一個(gè)字節(jié)時(shí),我們就不得考慮每個(gè)字節(jié)應(yīng)該以何種順序存放在內(nèi)存中了。
那么我們當(dāng)前的編譯器是采取大端存儲(chǔ)還是小端存儲(chǔ)呢?
別著急,回頭看看內(nèi)存中的值的順序,就能知道啦!
所以,我們當(dāng)前編譯器采用的是小端字節(jié)序。
百度曾經(jīng)出過(guò)一道筆試題,讓被試者設(shè)計(jì)一個(gè)程序來(lái)判斷當(dāng)前機(jī)器的字節(jié)序。
那么我們應(yīng)該如何做呢?
我們用1來(lái)進(jìn)行觀察。
所以,我們只需要拿出第一個(gè)字節(jié),看看里面存的是0還是1就知道了。
當(dāng)然,我們要實(shí)現(xiàn)的是查看一個(gè)機(jī)器字節(jié)序的功能,所以這里我們最好把程序封裝成一個(gè)函數(shù)。
看完了整數(shù)在內(nèi)存中是怎么存儲(chǔ)的,那么浮點(diǎn)型在內(nèi)存中又是怎么存儲(chǔ)的呢?
我們接下來(lái)就來(lái)看看。
首先我們常見(jiàn)的浮點(diǎn)數(shù)有:
小數(shù):3.1425926
科學(xué)計(jì)數(shù)法:1E10(1.0*1010)
在浮點(diǎn)型中,我們有float和double兩種類型。
而這兩種類型的范圍,我們用float.h來(lái)定義。
如果我們想看整數(shù)的最大值(最小值),我們就用INT_MAX(INT_MIN);并在前面包含頭文件
。
然后我們就能看到int類型所能存放的最大值啦!
那么,浮點(diǎn)數(shù)所能存放的最大最小值是多少呢?
同理,我們可以用FLT_MAX(或者FLT_MIN、DBL_MAX等),引用頭文件
就能得到他們的精度啦~
那么在內(nèi)存中浮點(diǎn)數(shù)的存儲(chǔ)和整數(shù)的存儲(chǔ)是一樣的嗎?
我們可以先通過(guò)一段代碼來(lái)進(jìn)行驗(yàn)證。
那么如果浮點(diǎn)型的存儲(chǔ)和整型不一樣,它又是怎么存儲(chǔ)的呢?
根據(jù)國(guó)際標(biāo)準(zhǔn)IEEE(電氣和電子工程協(xié)會(huì)) 754,任意一個(gè)二進(jìn)制浮點(diǎn)數(shù)V可以表示成下面的形式:
- (-1)S * M * 2E
(-1)s表示符號(hào)位,當(dāng)S=0,V為正數(shù);當(dāng)S=1,V為負(fù)數(shù)。
M表示有效數(shù)字,1≤M<2。
2E表示指數(shù)位。
我們以小數(shù)5.5來(lái)舉例。
同理,我們可以寫(xiě)出9.0的表示形式:
我們會(huì)發(fā)現(xiàn),其實(shí)任何一個(gè)浮點(diǎn)數(shù)都可以寫(xiě)成這種形式。
那么,只要我們把一個(gè)浮點(diǎn)數(shù)寫(xiě)成(-1)S * M * 2E這種形式,那么只要我們存儲(chǔ)了S、M、E的信息,就能還原出浮點(diǎn)數(shù)的值了。
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。
因?yàn)?≤M<2 即M可以寫(xiě)成1.XXXXXX 的形式,其中XXXXXX表示小數(shù)部分。
IEEE 754規(guī)定,在計(jì)算機(jī)內(nèi)部保存M時(shí),默認(rèn)這個(gè)數(shù)的第一位總是1,因此這個(gè)1和它后面的小數(shù)點(diǎn)可以被舍去,只保存后面的XXXXXX部分。
比如:當(dāng)我們要保存1.01的時(shí)候,只需要保存01,等到讀取的時(shí)候,再把前面的1.加上去。
這樣,我們就節(jié)省了1位有效數(shù)字。
以32位浮點(diǎn)數(shù)(float)為例,留給M只有23位,將第一位的1舍去以后,等于可以保存24位有效數(shù)字,這樣精度就得到了提高。
我們知道,對(duì)于指數(shù)E來(lái)說(shuō),它是可以去負(fù)數(shù)的,那么對(duì)于負(fù)數(shù)的E在存儲(chǔ)的時(shí)候,我們又應(yīng)該如何處理呢?
于是人們又想出了一個(gè)辦法,不如給這個(gè)E加上一個(gè)中間數(shù),讓E即使是最小的負(fù)數(shù),加上這個(gè)數(shù)之后也不會(huì)為負(fù)(等于0),那么就不會(huì)出現(xiàn)負(fù)數(shù)的情況了,只要我們?nèi)〕鰜?lái)的時(shí)候再把這個(gè)中間數(shù)加上就行了。
這樣,我們就可以把E作為一個(gè)無(wú)符號(hào)整數(shù)了。
如果E為8位,那么它的取值范圍就是:0~255,那么它的中間數(shù)就是127。
對(duì)于11位的E,它的取值范圍是0~2047,它的中間數(shù)就是1023。
比如:210的E是10,所以保存成32位浮點(diǎn)數(shù)時(shí),就保存成10+127=137,即10001001。
以5.5為例,我們可以在內(nèi)存中看到它的存儲(chǔ)。
并且我們可以發(fā)現(xiàn),對(duì)于浮點(diǎn)數(shù),它在內(nèi)存中的存儲(chǔ)也是有大小端的。
以上,我們就知道了浮點(diǎn)數(shù)在內(nèi)存中是怎么存的了。
那么它又是怎么取出來(lái)的呢?
按道理,我們是怎么放進(jìn)去的,就應(yīng)該是怎么取出來(lái)的。但是由于指數(shù)E比較特別,所以再取出來(lái)時(shí),又有一些特別的規(guī)定。
在內(nèi)存中取出E時(shí),我們有一下三種情況:
E不全為0或不全為1
這時(shí),E是怎么存進(jìn)來(lái)的,我們就按照原路取出來(lái)。
即指數(shù)E的計(jì)算值減去127(或1023),得到真實(shí)值,再將有效數(shù)字M前加上第一位的1。
比如: 0.5的二進(jìn)制形式為0.1,由于規(guī)定正數(shù)部分必須為1,即將小數(shù)點(diǎn)右移1位,則為1.0*2^(-1)。
同理,反過(guò)來(lái),我們可以通過(guò)這個(gè)二進(jìn)制序列得到0.5。
E全為0
當(dāng)E全為0的時(shí)候,說(shuō)明浮點(diǎn)數(shù)的指數(shù)E等于-127,而2-127是一個(gè)非常非常小的數(shù),已經(jīng)十分接近于0了。
所以這時(shí)候,我們就不再按照原來(lái)的計(jì)算方式,而是把浮點(diǎn)數(shù)的指數(shù)E直接記為1-127(或者1-1023), 并且有效數(shù)字M不再加上第一位的1,而是還原為0.XXXXXX的小數(shù)。這樣做是為了表示±0,以及無(wú)窮接近于0的很小的數(shù)。
E全為1
當(dāng)E全為1時(shí),則得到的是255,減去127得到的是128,而2128是一個(gè)非常大的數(shù)。所以這時(shí),如果有效數(shù)字M全為0,則該數(shù)就表示±無(wú)窮大(正負(fù)取決于符號(hào)位S)
當(dāng)我們了解完以上浮點(diǎn)型在內(nèi)存中的存儲(chǔ)規(guī)則之后,我們就可以理解之前哪個(gè)代碼打印出來(lái)的值了。
今天的文章就到這里啦!~
你學(xué)廢了嗎?記得點(diǎn)贊收藏加關(guān)注,在評(píng)論區(qū)留下你的腳印~
關(guān)注我!一起精進(jìn)C語(yǔ)言!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/119293.html
摘要:結(jié)構(gòu)體類型的特殊聲明在初階結(jié)構(gòu)體中,我們已經(jīng)將了結(jié)構(gòu)體類型是如何進(jìn)行聲明的,那么在這里,我們將講一些特殊的結(jié)構(gòu)體聲明不完全的聲明。所以我們應(yīng)該這樣寫(xiě)通過(guò)指針來(lái)找到下一個(gè)同類型結(jié)構(gòu)體的寫(xiě)法,我們就稱之為結(jié)構(gòu)體的自引用。 ...
摘要:插件開(kāi)發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實(shí)現(xiàn)文件分片斷點(diǎn)續(xù)傳。 Vue.js 插件開(kāi)發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。插....
摘要:的理解和區(qū)別代表有符號(hào),整數(shù)在內(nèi)存中存儲(chǔ)的二進(jìn)制位的最高位為符號(hào)位,表示負(fù)數(shù),表示正數(shù)。那接下來(lái)我們來(lái)學(xué)習(xí)數(shù)據(jù)在所開(kāi)辟的內(nèi)存空間時(shí)如何存儲(chǔ)的。請(qǐng)看下面例子為什么內(nèi)存中存儲(chǔ)的是補(bǔ)碼對(duì)于整數(shù)來(lái)說(shuō)數(shù)據(jù)存放內(nèi)存中其實(shí)存放的是補(bǔ)碼。 ...
摘要:中的詳解必修個(gè)多線程問(wèn)題總結(jié)個(gè)多線程問(wèn)題總結(jié)有哪些源代碼看了后讓你收獲很多,代碼思維和能力有較大的提升有哪些源代碼看了后讓你收獲很多,代碼思維和能力有較大的提升開(kāi)源的運(yùn)行原理從虛擬機(jī)工作流程看運(yùn)行原理。 自己實(shí)現(xiàn)集合框架 (三): 單鏈表的實(shí)現(xiàn) 自己實(shí)現(xiàn)集合框架 (三): 單鏈表的實(shí)現(xiàn) 基于 POI 封裝 ExcelUtil 精簡(jiǎn)的 Excel 導(dǎo)入導(dǎo)出 由于 poi 本身只是針對(duì)于 ...
閱讀 1534·2023-04-25 16:31
閱讀 2113·2021-11-24 10:33
閱讀 2807·2021-09-23 11:33
閱讀 2620·2021-09-23 11:31
閱讀 3027·2021-09-08 09:45
閱讀 2415·2021-09-06 15:02
閱讀 2730·2019-08-30 14:21
閱讀 2388·2019-08-30 12:56