3.1 大話C語言變量和數(shù)據(jù)類型
在《數(shù)據(jù)在內(nèi)存中的存儲》一節(jié)中講到:
●計(jì)算機(jī)要處理的數(shù)據(jù)(諸如數(shù)字、文字焙贷、符號撵割、圖形、音頻辙芍、視頻等)是以二進(jìn)制的形式存放在內(nèi)存中的啡彬;
●我們將8個(gè)比特(Bit)稱為一個(gè)字節(jié)(Byte),并將字節(jié)作為最小的可操作單元故硅。
我們不妨先從最簡單的整數(shù)說起庶灿,看看它是如何放到內(nèi)存中去的。
變量(Variable)
現(xiàn)實(shí)生活中我們會找一個(gè)小箱子來存放物品吃衅,一來顯得不那么凌亂往踢,二來方便以后找到。計(jì)算機(jī)也是這個(gè)道理徘层,我們需要先在內(nèi)存中找一塊區(qū)域峻呕,規(guī)定用它來存放整數(shù),并起一個(gè)好記的名字趣效,方便以后查找瘦癌。這塊區(qū)域就是“小箱子”,我們可以把整數(shù)放進(jìn)去了跷敬。 C語言中這樣在內(nèi)存中找一塊區(qū)域:
int又是一個(gè)新單詞讯私,它是 Integer 的簡寫,意思是整數(shù)西傀。a 是我們給這塊區(qū)域起的名字斤寇;當(dāng)然也可以叫其他名字,例如 abc池凄、mn123 等抡驼。 這個(gè)語句的意思是:在內(nèi)存中找一塊區(qū)域,命名為 a肿仑,用它來存放整數(shù)致盟。
注意 int 和 a 之間是有空格的,它們是兩個(gè)詞尤慰。
也注意最后的分號馏锡,int a表達(dá)了完整的意思,是一個(gè)語句伟端,要用分號來結(jié)束杯道。
不過int a;僅僅是在內(nèi)存中找了一塊可以保存整數(shù)的區(qū)域,那么如何將 123责蝠、100党巾、999 這樣的數(shù)字放進(jìn)去呢萎庭? C語言中這樣向內(nèi)存中放整數(shù):
=是一個(gè)新符號,它在數(shù)學(xué)中叫“等于號”齿拂,例如 1+2=3驳规,但在C語言中,這個(gè)過程叫做賦值(Assign)署海。賦值是指把數(shù)據(jù)放到內(nèi)存的過程吗购。 把上面的兩個(gè)語句連起來:
就把 123 放到了一塊叫做 a 的內(nèi)存區(qū)域。你也可以寫成一個(gè)語句:
a 中的整數(shù)不是一成不變的砸狞,只要我們需要捻勉,隨時(shí)可以更改。更改的方式就是再次賦值刀森,例如:
第二次賦值踱启,會把第一次的數(shù)據(jù)覆蓋(擦除)掉,也就是說撒强,a 中最后的值是9999禽捆,123笙什、1000 已經(jīng)不存在了飘哨,再也找不回來了。 因?yàn)?a 的值可以改變琐凭,所以我們給它起了一個(gè)形象的名字芽隆,叫做變量(Variable)。 int a;創(chuàng)造了一個(gè)變量 a统屈,我們把這個(gè)過程叫做變量定義胚吁。
a=123;把 123 交給了變量 a,我們把這個(gè)過程叫做給變量賦值愁憔;
又因?yàn)槭堑谝淮钨x值,也稱變量的初始化,或者賦初值怀喉。 你可以先定義變量胯杭,再初始化,例如:
也可以在定義的同時(shí)進(jìn)行初始化膜宋,例如:
這兩種方式是等價(jià)的窿侈。
數(shù)據(jù)類型(Data Type)
數(shù)據(jù)是放在內(nèi)存中的,變量是給這塊內(nèi)存起的名字秋茫,有了變量就可以找到并使用這份數(shù)據(jù)史简。但問題是,該如何使用呢肛著? 我們知道圆兵,諸如數(shù)字跺讯、文字、符號殉农、圖形抬吟、音頻、視頻等數(shù)據(jù)都是以二進(jìn)制形式存儲在內(nèi)存中的统抬,它們并沒有本質(zhì)上的區(qū)別火本,那么,00010000 該理解為數(shù)字16呢聪建,還是圖像中某個(gè)像素的顏色呢钙畔,還是要發(fā)出某個(gè)聲音呢?如果沒有特別指明金麸,我們并不知道擎析。 也就是說,內(nèi)存中的數(shù)據(jù)有多種解釋方式挥下,使用之前必須要確定揍魂;上面的int a;就表明,這份數(shù)據(jù)是整數(shù)棚瘟,不能理解為像素现斋、聲音等。int 有一個(gè)專業(yè)的稱呼偎蘸,叫做數(shù)據(jù)類型(Data Type)庄蹋。 顧名思義,數(shù)據(jù)類型用來說明數(shù)據(jù)的類型迷雪,確定了數(shù)據(jù)的解釋方式限书,讓計(jì)算機(jī)和程序員不會產(chǎn)生歧義。在C語言中章咧,有多種數(shù)據(jù)類型倦西,例如:
說 明 | 字符型 | 短整型 | 整型 | 長整型 | 單精度浮點(diǎn)型 | 雙精度浮點(diǎn)型 | 無類型 |
---|---|---|---|---|---|---|---|
數(shù)據(jù)類型 | char | short | int | long | float | double | void |
這些是最基本的數(shù)據(jù)類型,是C語言自帶的赁严,如果我們需要扰柠,還可以通過它們組成更加復(fù)雜的數(shù)據(jù)類型,后面我們會一一講解误澳。
連續(xù)定義多個(gè)變量
為了讓程序的書寫更加簡潔耻矮,C語言支持多個(gè)變量的連續(xù)定義,例如:
連續(xù)定義的多個(gè)變量以逗號,分隔忆谓,并且要擁有相同的數(shù)據(jù)類型裆装;變量可以初始化,也可以不初始化。
數(shù)據(jù)的長度(Length)
所謂數(shù)據(jù)長度(Length)哨免,是指數(shù)據(jù)占用多少個(gè)字節(jié)茎活。占用的字節(jié)越多,能存儲的數(shù)據(jù)就越多琢唾,對于數(shù)字來說载荔,值就會更大,反之能存儲的數(shù)據(jù)就有限采桃。 多個(gè)數(shù)據(jù)在內(nèi)存中是連續(xù)存儲的懒熙,彼此之間沒有明顯的界限,如果不明確指明數(shù)據(jù)的長度普办,計(jì)算機(jī)就不知道何時(shí)存取結(jié)束工扎。例如我們保存了一個(gè)整數(shù) 1000,它占用4個(gè)字節(jié)的內(nèi)存衔蹲,而讀取時(shí)卻認(rèn)為它占用3個(gè)字節(jié)或5個(gè)字節(jié)肢娘,這顯然是不正確的。 所以舆驶,在定義變量時(shí)還要指明數(shù)據(jù)的長度橱健。而這恰恰是數(shù)據(jù)類型的另外一個(gè)作用。數(shù)據(jù)類型除了指明數(shù)據(jù)的解釋方式沙廉,還指明了數(shù)據(jù)的長度拘荡。因?yàn)樵贑語言中,每一種數(shù)據(jù)類型所占用的字節(jié)數(shù)都是固定的蓝仲,知道了數(shù)據(jù)類型俱病,也就知道了數(shù)據(jù)的長度官疲。 在32位環(huán)境中袱结,各種數(shù)據(jù)類型的長度一般如下:
說 明 | 字符型 | 短整型 | 整型 | 長整型 | 單精度浮點(diǎn)型 | 雙精度浮點(diǎn)型 |
---|---|---|---|---|---|---|
數(shù)據(jù)類型 | char | short | int | long | float | double |
長 度 | 1 | 2 | 4 | 4 | 4 | 8 |
C語言有多少種數(shù)據(jù)類型,每種數(shù)據(jù)類型長度是多少途凫、該如何使用垢夹,這是每一位C程序員都必須要掌握的,后續(xù)我們會一一講解维费。
最后的總結(jié)
數(shù)據(jù)是放在內(nèi)存中的果元,在內(nèi)存中存取數(shù)據(jù)要明確三件事情:數(shù)據(jù)存儲在哪里、數(shù)據(jù)的長度以及數(shù)據(jù)的處理方式犀盟。 變量名不僅僅是為數(shù)據(jù)起了一個(gè)好記的名字而晒,還告訴我們數(shù)據(jù)存儲在哪里,使用數(shù)據(jù)時(shí)阅畴,只要提供變量名即可倡怎;而數(shù)據(jù)類型則指明了數(shù)據(jù)的長度和處理方式。所以諸如int n;、char c;监署、float money;這樣的形式就確定了數(shù)據(jù)在內(nèi)存中的所有要素颤专。 C語言提供的多種數(shù)據(jù)類型讓程序更加靈活和高效,同時(shí)也增加了學(xué)習(xí)成本钠乏。而有些編程語言栖秕,例如PHP、JavaScript等晓避,在定義變量時(shí)不需要指明數(shù)據(jù)類型簇捍,編譯器會根據(jù)賦值情況自動(dòng)推演出數(shù)據(jù)類型,更加智能俏拱。 除了C語言垦写,Java、C++彰触、C#,等在定義變量時(shí)也必須指明數(shù)據(jù)類型梯投,這樣的編程語言稱為強(qiáng)類型語言。
而PHP况毅、JavaScript等在定義變量時(shí)不必指明數(shù)據(jù)類型分蓖,編譯系統(tǒng)會自動(dòng)推演,這樣的編程語言稱為弱類型語言尔许。 強(qiáng)類型語言一旦確定了數(shù)據(jù)類型么鹤,就不能再賦給其他類型的數(shù)據(jù),除非對數(shù)據(jù)類型進(jìn)行轉(zhuǎn)換味廊。弱類型語言沒有這種限制蒸甜,一個(gè)變量,可以先賦給一個(gè)整數(shù)余佛,然后再賦給一個(gè)字符串柠新。 最后需要說明的是:數(shù)據(jù)類型只在定義變量時(shí)指明,而且必須指明辉巡;使用變量時(shí)無需再指明恨憎,因?yàn)榇藭r(shí)的數(shù)據(jù)類型已經(jīng)確定了。
3.2 在屏幕上輸出各種類型的數(shù)據(jù)
在《第一個(gè)C語言程序》一節(jié)中郊楣,我們使用 puts 來輸出字符串憔恳。puts 是 output string 的縮寫,只能用來輸出字符串净蚤,不能輸出整數(shù)钥组、小數(shù)、字符等今瀑,我們需要用另外一個(gè)函數(shù)程梦,那就是 printf腔丧。 printf 比 puts 更加強(qiáng)大,不僅可以輸出字符串作烟,還可以輸出整數(shù)愉粤、小數(shù)、單個(gè)字符等拿撩,并且輸出格式也可以自己定義压恒,例如:
●以十進(jìn)制、八進(jìn)制、十六進(jìn)制形式輸出;
●要求輸出的數(shù)字占 n 個(gè)字符的位置腺逛;
●控制小數(shù)的位數(shù)抛杨。
printf 是 print format 的縮寫慨绳,意思是“格式化打印”。這里所謂的“打印”就是在屏幕上顯示內(nèi)容,與“輸出”的含義相同讨韭,所以我們一般稱 printf 是用來格式化輸出的狰闪。 先來看一個(gè)簡單的例子:
這個(gè)語句可以在屏幕上顯示“C語言中文網(wǎng)”丽声,與puts("C語言中文網(wǎng)");的效果類似。 輸出變量 abc 的值:
這里就比較有趣了。先來看%d喊巍,d 是 decimal 的縮寫款咖,意思是十進(jìn)制數(shù)铐殃,%d 表示以十進(jìn)制整數(shù)的形式輸出海洼。輸出什么呢富腊?輸出變量 abc 的值。%d 與 abc 是對應(yīng)的赘被,也就是說,會用 abc 的值來替換 %d。 再來看個(gè)復(fù)雜點(diǎn)的:
會在屏幕上顯示: The value of abc is 999 ! 你看,字符串 "The value of abc is %d !" 中的 %d 被替換成了 abc 的值易迹,其他字符沒有改變供炼。這說明 %d 比較特殊,不會原樣輸出句伶,會被替換成對應(yīng)的變量的值劲蜻。 再來看:
會在屏幕上顯示: a=100, b=200, c=300 再次證明了 %d 與后面的變量是一一對應(yīng)的,第一個(gè) %d 對應(yīng)第一個(gè)變量考余,第二個(gè) %d 對應(yīng)第二個(gè)變量…… %d稱為格式控制符先嬉,它指明了以何種形式輸出數(shù)據(jù)。格式控制符均以%開頭楚堤,后跟其他字符疫蔓。%d 表示以十進(jìn)制形式輸出一個(gè)整數(shù)。除了 %d身冬,printf 支持更多的格式控制衅胀,例如:
●%c:輸出一個(gè)字符。c 是 character 的簡寫酥筝。
●%s:輸出一個(gè)字符串滚躯。s 是 string 的簡寫。
●%f:輸出一個(gè)小數(shù)嘿歌。f 是 float 的簡寫掸掏。
除了這些,printf 支持更加復(fù)雜和優(yōu)美的輸出格式宙帝,考慮到讀者的基礎(chǔ)暫時(shí)不夠丧凤。 我們把代碼補(bǔ)充完整,體驗(yàn)一下:
輸出結(jié)果: n=100, c=@, money=93.959999 要點(diǎn)提示: 1) \n是一個(gè)整體步脓,組合在一起表示一個(gè)換行字符愿待。換行符是 ASCII 編碼中的一個(gè)控制字符,無法在鍵盤上直接輸入靴患,只能用這種特殊的方法表示仍侥,被稱為轉(zhuǎn)義字符,請大家暫時(shí)先記住\n的含義蚁廓。
所謂換行访圃,就是讓文本從下一行的開頭輸出,相當(dāng)于在編輯 Word 或者 TXT 文檔時(shí)按下回車鍵相嵌。
puts 輸出完成后會自動(dòng)換行腿时,而 printf 不會,要自己添加換行符饭宾,這是 puts 和 printf 在輸出字符串時(shí)的一個(gè)區(qū)別批糟。
//后面的為注釋。注釋用來說明代碼是什么意思看铆,起到提示的作用徽鼎,可以幫助我們理解代碼。注釋雖然也是代碼的一部分弹惦,但是它并不會給程序帶來任何影響否淤,編譯器在編譯階段會忽略注釋的內(nèi)容,或者說刪除注釋的內(nèi)容棠隐。
money 的輸出值并不是 93.96石抡,而是一個(gè)非常接近的值,這與小數(shù)本身的存儲機(jī)制有關(guān)助泽,這種機(jī)制導(dǎo)致很多小數(shù)不能被精確地表示啰扛,即使像 93.96 這種簡單的小數(shù)也不行。我們將在《小數(shù)在內(nèi)存中是如何存儲的嗡贺,揭秘諾貝爾獎(jiǎng)級別的設(shè)計(jì)(長篇神文)》一節(jié)詳細(xì)介紹隐解。 我們也可以不用變量,將數(shù)據(jù)直接輸出:
輸出結(jié)果與上面相同诫睬。 在以后的編程中煞茫,我們會經(jīng)常使用 printf,說它是C語言中使用頻率最高的一個(gè)函數(shù)一點(diǎn)也不為過摄凡,每個(gè)C語言程序員都應(yīng)該掌握 printf 的用法续徽,這是最基本的技能。 不過 printf 的用法比較靈活架谎,也比較復(fù)雜炸宵,初學(xué)者知識儲備不足,不能一下子掌握谷扣,目前大家只需要掌握最基本的用法土全,以后隨著編程知識的學(xué)習(xí),我們會逐步介紹更加高級的用法会涎,最終讓大家完全掌握 printf裹匙。
【腦筋急轉(zhuǎn)彎】%ds輸出什么
%d 輸出整數(shù),%s 輸出字符串末秃,那么 %ds 輸出什么呢概页? 我們不妨先來看一個(gè)例子:
運(yùn)行結(jié)果: a=1234s
從輸出結(jié)果可以發(fā)現(xiàn),%d被替換成了變量 a 的值练慕,而s沒有變惰匙,原樣輸出了技掏。這是因?yàn)椋?%d才是格式控制符,%ds在一起沒有意義项鬼,s僅僅是跟在%d后面的一個(gè)普通字符哑梳,所以會原樣輸出。
【拓展】如何在字符串中書寫長文本
假設(shè)現(xiàn)在我們要輸出一段比較長的文本绘盟,它的內(nèi)容為:
C語言中文網(wǎng)鸠真,一個(gè)學(xué)習(xí)C語言和C++的網(wǎng)站,他們堅(jiān)持用工匠的精神來打磨每一套教程龄毡。堅(jiān)持做好一件事情吠卷,做到極致,讓自己感動(dòng)沦零,讓用戶心動(dòng)祭隔,這就是足以傳世的作品!C語言中文網(wǎng)的網(wǎng)址是:http://c.biancheng.net
如果將這段文本放在一個(gè)字符串中蠢终,會顯得比較臃腫序攘,格式也不好看,就像下面這樣:
超出編輯窗口寬度的文本換行
超出編輯窗口寬度的文本隱藏
當(dāng)文本超出編輯窗口的寬度時(shí)寻拂,可以選擇將文本換行程奠,也可以選擇將文本隱藏(可以在編輯器里面自行設(shè)置)祭钉,但是不管哪種形式慌核,在一個(gè)字符串里書寫長文本總是不太美觀距境。 當(dāng)然,你可以多寫幾個(gè) puts 函數(shù)垮卓,就像下面這樣:
我不否認(rèn)這種寫法也比較美觀垫桂,但是這里我要講的是另外一種寫法:
在 puts 函數(shù)中,可以將一個(gè)較長的字符串分割成幾個(gè)較短的字符串,這樣會使得長文本的格式更加整齊灭将。 注意疼鸟,這只是形式上的分割,編譯器在編譯階段會將它們合并為一個(gè)字符串,它們放在一塊連續(xù)的內(nèi)存中吴攒。 多個(gè)字符串并不一定非得換行张抄,也可以將它們寫在一行中,例如:
本節(jié)講到的 puts舶斧、printf欣鳖,以及后面要講到的 fprintf察皇、fputs 等與字符串輸出有關(guān)的函數(shù)茴厉,都支持這種寫法。
3.3 C語言中的整數(shù)(short,int,long)
整數(shù)是編程中常用的一種數(shù)據(jù)什荣,C語言通常使用int來定義整數(shù)(int 是 integer 的簡寫)矾缓,這在《大話C語言變量和數(shù)據(jù)類型》中已經(jīng)進(jìn)行了詳細(xì)講解。 在現(xiàn)代操作系統(tǒng)中稻爬,int 一般占用 4 個(gè)字節(jié)(Byte)的內(nèi)存嗜闻,共計(jì) 32 位(Bit)。如果不考慮正負(fù)數(shù)桅锄,當(dāng)所有的位都為 1 時(shí)它的值最大琉雳,為 232-1 = 4,294,967,295 ≈ 43億,這是一個(gè)很大的數(shù)友瘤,實(shí)際開發(fā)中很少用到翠肘,而諸如 1、99辫秧、12098 等較小的數(shù)使用頻率反而較高束倍。 使用 4 個(gè)字節(jié)保存較小的整數(shù)綽綽有余,會空閑出兩三個(gè)字節(jié)來盟戏,這些字節(jié)就白白浪費(fèi)掉了绪妹,不能再被其他數(shù)據(jù)使用。現(xiàn)在個(gè)人電腦的內(nèi)存都比較大了柿究,配置低的也有 4G邮旷,浪費(fèi)一些內(nèi)存不會帶來明顯的損失;而在C語言被發(fā)明的早期蝇摸,或者在單片機(jī)和嵌入式系統(tǒng)中婶肩,內(nèi)存都是非常稀缺的資源,所有的程序都在盡力節(jié)省內(nèi)存探入。 反過來說狡孔,43 億雖然已經(jīng)很大,但要表示全球人口數(shù)量還是不夠蜂嗽,必須要讓整數(shù)占用更多的內(nèi)存苗膝,才能表示更大的值,比如占用 6 個(gè)字節(jié)或者 8 個(gè)字節(jié)植旧。 讓整數(shù)占用更少的內(nèi)存可以在 int 前邊加 short辱揭,讓整數(shù)占用更多的內(nèi)存可以在 int 前邊加 long离唐,例如:
這樣 a、b问窃、c 只占用 2 個(gè)字節(jié)的內(nèi)存亥鬓,而 m、n域庇、p 可能會占用 8 個(gè)字節(jié)的內(nèi)存嵌戈。 也可以將 int 省略,只寫 short 和 long听皿,如下所示:
這樣的寫法更加簡潔熟呛,實(shí)際開發(fā)中常用。 int 是基本的整數(shù)類型尉姨,short 和 long 是在 int 的基礎(chǔ)上進(jìn)行的擴(kuò)展庵朝,short 可以節(jié)省內(nèi)存,long 可以容納更大的值又厉。 short九府、int、long 是C語言中常見的整數(shù)類型覆致,其中 int 稱為整型侄旬,short 稱為短整型,long 稱為長整型篷朵。
整型的長度
細(xì)心的讀者可能會發(fā)現(xiàn)勾怒,上面我們在描述 short、int声旺、long 類型的長度時(shí)笔链,只對 short 使用肯定的說法,而對 int腮猖、long 使用了“一般”或者“可能”等不確定的說法鉴扫。這種描述的言外之意是,只有 short 的長度是確定的澈缺,是兩個(gè)字節(jié)坪创,而 int 和 long 的長度無法確定,在不同的環(huán)境下有不同的表現(xiàn)姐赡。
一種數(shù)據(jù)類型占用的字節(jié)數(shù)莱预,稱為該數(shù)據(jù)類型的長度。例如项滑,short 占用 2 個(gè)字節(jié)的內(nèi)存依沮,那么它的長度就是 2。
實(shí)際情況也確實(shí)如此,C語言并沒有嚴(yán)格規(guī)定 short危喉、int宋渔、long 的長度,只做了寬泛的限制:
●short 至少占用 2 個(gè)字節(jié)辜限。
●int 建議為一個(gè)機(jī)器字長皇拣。32 位環(huán)境下機(jī)器字長為 4 字節(jié),64 位環(huán)境下機(jī)器字長為 8 字節(jié)薄嫡。
●short 的長度不能大于 int氧急,long 的長度不能小于 int。
總結(jié)起來岂座,它們的長度(所占字節(jié)數(shù))關(guān)系為:
這就意味著态蒂,short 并不一定真的”短“,long 也并不一定真的”長“费什,它們有可能和 int 占用相同的字節(jié)數(shù)。 在 16 位環(huán)境下手素,short 的長度為 2 個(gè)字節(jié)鸳址,int 也為 2 個(gè)字節(jié),long 為 4 個(gè)字節(jié)泉懦。16 位環(huán)境多用于單片機(jī)和低級嵌入式系統(tǒng)稿黍,在PC和服務(wù)器上已經(jīng)見不到了。 對于 32 位的 Windows崩哩、Linux 和 Mac OS巡球,short 的長度為 2 個(gè)字節(jié),int 為 4 個(gè)字節(jié)邓嘹,long 也為 4 個(gè)字節(jié)酣栈。PC和服務(wù)器上的 32 位系統(tǒng)占有率也在慢慢下降,嵌入式系統(tǒng)使用 32 位越來越多汹押。 在 64 位環(huán)境下矿筝,不同的操作系統(tǒng)會有不同的結(jié)果,如下所示:
操作系統(tǒng) | short | int | long |
---|---|---|---|
Win64(64位 Windows) | 2 | 4 | 4 |
類Unix系統(tǒng)(包括 Unix棚贾、Linux窖维、Mac OS、BSD妙痹、Solaris 等) | 2 | 4 | 8 |
目前我們使用較多的PC系統(tǒng)為 Win XP破停、Win 7复凳、Win 8、Win 10、Mac OS蚯撩、Linux殴玛,在這些系統(tǒng)中,short 和 int 的長度都是固定的,分別為 2 和 4水评,大家可以放心使用,只有 long 的長度在 Win64 和類 Unix 系統(tǒng)下會有所不同媚送,使用時(shí)要注意移植性中燥。
sizeof 操作符
獲取某個(gè)數(shù)據(jù)類型的長度可以使用 sizeof 操作符,如下所示:
在 32 位環(huán)境以及 Win64 環(huán)境下的運(yùn)行結(jié)果為:
short=2, int=4, long=4, char=1
在 64 位 Linux 和 Mac OS 下的運(yùn)行結(jié)果為:
short=2, int=4, long=8, char=1
sizeof 用來獲取某個(gè)數(shù)據(jù)類型或變量所占用的字節(jié)數(shù)塘偎,如果后面跟的是變量名稱疗涉,那么可以省略( ),如果跟的是數(shù)據(jù)類型吟秩,就必須帶上( )咱扣。 需要注意的是,sizeof 是C語言中的操作符涵防,不是函數(shù)闹伪,所以可以不帶( ),后面會詳細(xì)講解壮池。
不同整型的輸出
使用不同的格式控制符可以輸出不同類型的整數(shù)偏瓤,它們分別是:
●%hd用來輸出 short int 類型,hd 是 short decimal 的簡寫椰憋;
●%d用來輸出 int 類型厅克,d 是 decimal 的簡寫;
●%ld用來輸出 long int 類型橙依,ld 是 long decimal 的簡寫证舟。
下面的例子演示了不同整型的輸出:
運(yùn)行結(jié)果: a=10, b=100, c=9437
在編寫代碼的過程中,我建議將格式控制符和數(shù)據(jù)類型嚴(yán)格對應(yīng)起來窗骑,養(yǎng)成良好的編程習(xí)慣女责。當(dāng)然,如果你不嚴(yán)格對應(yīng)慧域,一般也不會導(dǎo)致錯(cuò)誤鲤竹,例如,很多初學(xué)者都使用%d輸出所有的整數(shù)類型昔榴,請看下面的例子:
運(yùn)行結(jié)果仍然是: a=10, b=100, c=9437
當(dāng)使用%d輸出 short辛藻,或者使用%ld輸出 short、int 時(shí)互订,不管值有多大吱肌,都不會發(fā)生錯(cuò)誤,因?yàn)楦袷娇刂品銐蛉菁{這些值仰禽。 當(dāng)使用%hd輸出 int氮墨、long纺蛆,或者使用%d輸出 long 時(shí),如果要輸出的值比較泄婢尽(就像上面的情況)桥氏,一般也不會發(fā)生錯(cuò)誤,如果要輸出的值比較大猛铅,就很有可能發(fā)生錯(cuò)誤字支,例如:
在 64 位 Linux 和 Mac OS 下(long 的長度為 8)的運(yùn)行結(jié)果為:
m=-21093, n=4556 n=-1898311220
輸出結(jié)果完全是錯(cuò)誤的,這是因?yàn)?hd容納不下 m 和 n 的值奸忽,%d也容納不下 n 的值堕伪。 讀者需要注意,當(dāng)格式控制符和數(shù)據(jù)類型不匹配時(shí)栗菜,編譯器會給出警告欠雌,提示程序員可能會存在風(fēng)險(xiǎn)。
編譯器的警告是分等級的疙筹,不同程度的風(fēng)險(xiǎn)被劃分成了不同的警告等級富俄,而使用%d輸出 short 和 long 類型的風(fēng)險(xiǎn)較低,如果你的編譯器設(shè)置只對較高風(fēng)險(xiǎn)的操作發(fā)出警告腌歉,那么此處你就看不到警告信息蛙酪。
3.4 C語言中的二進(jìn)制數(shù)、八進(jìn)制數(shù)和十六進(jìn)制數(shù)
C語言中的整數(shù)除了可以使用十進(jìn)制翘盖,還可以使用二進(jìn)制、八進(jìn)制和十六進(jìn)制凹蜂。
二進(jìn)制數(shù)馍驯、八進(jìn)制數(shù)和十六進(jìn)制數(shù)的表示
一個(gè)數(shù)字默認(rèn)就是十進(jìn)制的,表示一個(gè)十進(jìn)制數(shù)字不需要任何特殊的格式玛痊。但是汰瘫,表示一個(gè)二進(jìn)制、八進(jìn)制或者十六進(jìn)制數(shù)字就不一樣了擂煞,為了和十進(jìn)制數(shù)字區(qū)分開來混弥,必須采用某種特殊的寫法,具體來說对省,就是在數(shù)字前面加上特定的字符蝗拿,也就是加前綴。
- 二進(jìn)制
二進(jìn)制由 0 和 1 兩個(gè)數(shù)字組成蒿涎,使用時(shí)必須以0b或0B(不區(qū)分大小寫)開頭哀托,例如:
讀者請注意,標(biāo)準(zhǔn)的C語言并不支持上面的二進(jìn)制寫法劳秋,只是有些編譯器自己進(jìn)行了擴(kuò)展仓手,才支持二進(jìn)制數(shù)字胖齐。換句話說,并不是所有的編譯器都支持二進(jìn)制數(shù)字嗽冒,只有一部分編譯器支持呀伙,并且跟編譯器的版本有關(guān)系。
下面是實(shí)際測試的結(jié)果:
●Visual C++ 6.0 不支持添坊。
●Visual Studio 2015 支持剿另,但是 Visual Studio 2010 不支持;可以認(rèn)為帅腌,高版本的 Visual Studio 支持二進(jìn)制數(shù)字驰弄,低版本的 Visual Studio 不支持。
●GCC 4.8.2 支持速客,但是 GCC 3.4.5 不支持戚篙;可以認(rèn)為,高版本的 GCC 支持二進(jìn)制數(shù)字溺职,低版本的 GCC 不支持岔擂。
●LLVM/Clang 支持(內(nèi)嵌于 Mac OS 下的 Xcode 中)。
- 八進(jìn)制
八進(jìn)制由 0~7 八個(gè)數(shù)字組成浪耘,使用時(shí)必須以0開頭(注意是數(shù)字 0乱灵,不是字母 o),例如:
- 十六進(jìn)制
十六進(jìn)制由數(shù)字 0~9七冲、字母 A~F 或 a~f(不區(qū)分大小寫)組成痛倚,使用時(shí)必須以0x或0X(不區(qū)分大小寫)開頭,例如:
- 十進(jìn)制
十進(jìn)制由 0~9 十個(gè)數(shù)字組成澜躺,沒有任何前綴蝉稳,和我們平時(shí)的書寫格式一樣,不再贅述掘鄙。
二進(jìn)制數(shù)耘戚、八進(jìn)制數(shù)和十六進(jìn)制數(shù)的輸出
C語言中常用的整數(shù)有 short、int 和 long 三種類型操漠,通過 printf 函數(shù)收津,可以將它們以八進(jìn)制、十進(jìn)制和十六進(jìn)制的形式輸出浊伙。上節(jié)我們講解了如何以十進(jìn)制的形式輸出撞秋,這節(jié)我們重點(diǎn)講解如何以八進(jìn)制和十六進(jìn)制的形式輸出,下表列出了不同類型的整數(shù)吧黄、以不同進(jìn)制的形式輸出時(shí)對應(yīng)的格式控制符:
short | int | long | |
---|---|---|---|
八進(jìn)制 | %ho | %o | %lo |
十進(jìn)制 | %hd | %d | %ld |
十六進(jìn)制 | %hx 或者 %hX | %x 或者 %X | %lx 或者 %lX |
十六進(jìn)制數(shù)字的表示用到了英文字母部服,有大小寫之分,要在格式控制符中體現(xiàn)出來:
●%hx拗慨、%x 和 %lx 中的x小寫廓八,表明以小寫字母的形式輸出十六進(jìn)制數(shù)奉芦;
●%hX、%X 和 %lX 中的X大寫剧蹂,表明以大寫字母的形式輸出十六進(jìn)制數(shù)声功。
八進(jìn)制數(shù)字和十進(jìn)制數(shù)字不區(qū)分大小寫,所以格式控制符都用小寫形式宠叼。如果你比較叛逆先巴,想使用大寫形式,那么行為是未定義的冒冬,請你慎重:
●有些編譯器支持大寫形式伸蚯,只不過行為和小寫形式一樣;
●有些編譯器不支持大寫形式简烤,可能會報(bào)錯(cuò)剂邮,也可能會導(dǎo)致奇怪的輸出。
注意横侦,雖然部分編譯器支持二進(jìn)制數(shù)字的表示挥萌,但是卻不能使用 printf 函數(shù)輸出二進(jìn)制,這一點(diǎn)比較遺憾枉侧。當(dāng)然引瀑,通過轉(zhuǎn)換函數(shù)可以將其它進(jìn)制數(shù)字轉(zhuǎn)換成二進(jìn)制數(shù)字,并以字符串的形式存儲榨馁,然后在 printf 函數(shù)中使用%s輸出即可憨栽。考慮到讀者的基礎(chǔ)還不夠翼虫,這里就先不講這種方法了徒像。 【實(shí)例】以不同進(jìn)制的形式輸出整數(shù):
運(yùn)行結(jié)果:
a=126, b=2713, c=7325603
a=86, b=1483, c=1944451
a=56, b=5cb, c=1dab83
a=56, b=5CB, c=1DAB83
從這個(gè)例子可以發(fā)現(xiàn),一個(gè)數(shù)字不管以何種進(jìn)制來表示蛙讥,都能夠以任意進(jìn)制的形式輸出。數(shù)字在內(nèi)存中始終以二進(jìn)制的形式存儲灭衷,其它進(jìn)制的數(shù)字在存儲前都必須轉(zhuǎn)換為二進(jìn)制形式次慢;同理,一個(gè)數(shù)字在輸出時(shí)要進(jìn)行逆向的轉(zhuǎn)換翔曲,也就是從二進(jìn)制轉(zhuǎn)換為其他進(jìn)制迫像。
輸出時(shí)加上前綴
請讀者注意觀察上面的例子,會發(fā)現(xiàn)有一點(diǎn)不完美瞳遍,如果只看輸出結(jié)果:
●對于八進(jìn)制數(shù)字闻妓,它沒法和十進(jìn)制、十六進(jìn)制區(qū)分掠械,因?yàn)榘诉M(jìn)制由缆、十進(jìn)制和十六進(jìn)制都包含 0~7 這幾個(gè)數(shù)字注祖。
●對于十進(jìn)制數(shù)字,它沒法和十六進(jìn)制區(qū)分均唉,因?yàn)槭M(jìn)制也包含 0~9 這幾個(gè)數(shù)字是晨。如果十進(jìn)制數(shù)字中還不包含 8 和 9,那么也不能和八進(jìn)制區(qū)分了舔箭。
●對于十六進(jìn)制數(shù)字罩缴,如果沒有包含 a~f 或者 A~F,那么就無法和十進(jìn)制區(qū)分层扶,如果還不包含 8 和 9箫章,那么也不能和八進(jìn)制區(qū)分了。
區(qū)分不同進(jìn)制數(shù)字的一個(gè)簡單辦法就是镜会,在輸出時(shí)帶上特定的前綴檬寂。在格式控制符中加上#即可輸出前綴,例如 %#x稚叹、%#o焰薄、%#lX、%#ho 等扒袖,請看下面的代碼:
運(yùn)行結(jié)果:
a=0126, b=02713, c=07325603
a=86, b=1483, c=1944451
a=0x56, b=0x5cb, c=0x1dab83
a=0X56, b=0X5CB, c=0X1DAB83
十進(jìn)制數(shù)字沒有前綴塞茅,所以不用加#。如果你加上了季率,那么它的行為是未定義的野瘦,有的編譯器支持十進(jìn)制加#,只不過輸出結(jié)果和沒有加#一樣飒泻,有的編譯器不支持加#鞭光,可能會報(bào)錯(cuò),也可能會導(dǎo)致奇怪的輸出泞遗;但是惰许,大部分編譯器都能正常輸出,不至于當(dāng)成一種錯(cuò)誤史辙。
3.5 C語言中的正負(fù)數(shù)及其輸出
在數(shù)學(xué)中汹买,數(shù)字有正負(fù)之分。在C語言中也是一樣聊倔,short晦毙、int、long 都可以帶上正負(fù)號耙蔑,例如:
如果不帶正負(fù)號见妒,默認(rèn)就是正數(shù)。 符號也是數(shù)字的一部分甸陌,也要在內(nèi)存中體現(xiàn)出來须揣。符號只有正負(fù)兩種情況盐股,用1位(Bit)就足以表示;C語言規(guī)定返敬,把內(nèi)存的最高位作為符號位遂庄。以 int 為例,它占用 32 位的內(nèi)存劲赠,0~30 位表示數(shù)值涛目,31 位表示正負(fù)號。如下圖所示:
在編程語言中凛澎,計(jì)數(shù)往往是從0開始霹肝,例如字符串 "abc123",我們稱第 0 個(gè)字符是 a塑煎,第 1 個(gè)字符是 b沫换,第 5 個(gè)字符是 3。這和我們平時(shí)從 1 開始計(jì)數(shù)的習(xí)慣不一樣最铁,大家要慢慢適應(yīng)讯赏,培養(yǎng)編程思維。
C語言規(guī)定冷尉,在符號位中漱挎,用 0 表示正數(shù),用 1 表示負(fù)數(shù)雀哨。例如 int 類型的 -10 和 +16 在內(nèi)存中的表示如下:
short磕谅、int 和 long 類型默認(rèn)都是帶符號位的,符號位以外的內(nèi)存才是數(shù)值位雾棺。如果只考慮正數(shù)膊夹,那么各種類型能表示的數(shù)值范圍(取值范圍)就比原來小了一半。 但是在很多情況下捌浩,我們非常確定某個(gè)數(shù)字只能是正數(shù)放刨,比如班級學(xué)生的人數(shù)、字符串的長度尸饺、內(nèi)存地址等宏榕,這個(gè)時(shí)候符號位就是多余的了,就不如刪掉符號位侵佃,把所有的位都用來存儲數(shù)值,這樣能表示的數(shù)值范圍更大(大一倍)奠支。 C語言允許我們這樣做馋辈,如果不希望設(shè)置符號位,可以在數(shù)據(jù)類型前面加上 unsigned 關(guān)鍵字倍谜,例如:
這樣迈螟,short叉抡、int、long 中就沒有符號位了答毫,所有的位都用來表示數(shù)值褥民,正數(shù)的取值范圍更大了。這也意味著洗搂,使用了 unsigned 后只能表示正數(shù)消返,不能再表示負(fù)數(shù)了。 如果將一個(gè)數(shù)字分為符號和數(shù)值兩部分耘拇,那么不加 unsigned 的數(shù)字稱為有符號數(shù)撵颊,能表示正數(shù)和負(fù)數(shù),加了 unsigned 的數(shù)字稱為無符號數(shù)惫叛,只能表示正數(shù)倡勇。 請讀者注意一個(gè)小細(xì)節(jié),如果是unsigned int類型嘉涌,那么可以省略 int 妻熊,只寫 unsigned,例如:
無符號數(shù)的輸出
無符號數(shù)可以以八進(jìn)制仑最、十進(jìn)制和十六進(jìn)制的形式輸出扔役,它們對應(yīng)的格式控制符分別為:
unsigned short | unsigned int | unsigned long | |
---|---|---|---|
八進(jìn)制 | %ho | %o | %lo |
十進(jìn)制 | %hu | %u | %lu |
十六進(jìn)制 | %hx 或者 %hX | %x 或者 %X | %lx 或者 %lX |
上節(jié)我們也講到了不同進(jìn)制形式的輸出,但是上節(jié)我們還沒有講到正負(fù)數(shù)词身,所以也沒有關(guān)心這一點(diǎn)厅目,只是“籠統(tǒng)”地介紹了一遍。現(xiàn)在本節(jié)已經(jīng)講到了正負(fù)數(shù)法严,那我們就再深入地說一下损敷。 嚴(yán)格來說,格式控制符和整數(shù)的符號是緊密相關(guān)的深啤,具體就是:
●%d 以十進(jìn)制形式輸出有符號數(shù)拗馒;
●%u 以十進(jìn)制形式輸出無符號數(shù);
●%o 以八進(jìn)制形式輸出無符號數(shù)溯街;
●%x 以十六進(jìn)制形式輸出無符號數(shù)诱桂。
那么,如何以八進(jìn)制和十六進(jìn)制形式輸出有符號數(shù)呢呈昔?很遺憾挥等,printf 并不支持,也沒有對應(yīng)的格式控制符堤尾。在實(shí)際開發(fā)中肝劲,也基本沒有“輸出負(fù)的八進(jìn)制數(shù)或者十六進(jìn)制數(shù)”這樣的需求,我想可能正是因?yàn)檫@一點(diǎn),printf 才沒有提供對應(yīng)的格式控制符辞槐。 下表全面地總結(jié)了不同類型的整數(shù)掷漱,以不同進(jìn)制的形式輸出時(shí)對應(yīng)的格式控制符(--表示沒有對應(yīng)的格式控制符)。
short | int | long | unsigned short | unsigned int | unsigned long | |
---|---|---|---|---|---|---|
八進(jìn)制 | -- | -- | -- | %ho | %o | %lo |
十進(jìn)制 | %hd | %d | %ld | %hu | %u | %lu |
十六進(jìn)制 | -- | -- | -- | %hx 或者 %hX | %x 或者 %X | %lx 或者 %lX |
有讀者可能會問榄檬,上節(jié)我們也使用 %o 和 %x 來輸出有符號數(shù)了卜范,為什么沒有發(fā)生錯(cuò)誤呢?這是因?yàn)椋?/p>
●當(dāng)以有符號數(shù)的形式輸出時(shí)鹿榜,printf 會讀取數(shù)字所占用的內(nèi)存海雪,并把最高位作為符號位,把剩下的內(nèi)存作為數(shù)值位犬缨;
●當(dāng)以無符號數(shù)的形式輸出時(shí)喳魏,printf 也會讀取數(shù)字所占用的內(nèi)存,并把所有的內(nèi)存都作為數(shù)值位對待怀薛。
對于一個(gè)有符號的正數(shù)刺彩,它的符號位是 0,當(dāng)按照無符號數(shù)的形式讀取時(shí)枝恋,符號位就變成了數(shù)值位创倔,但是該位恰好是 0 而不是 1,所以對數(shù)值不會產(chǎn)生影響焚碌,這就好比在一個(gè)數(shù)字前面加 0畦攘,有多少個(gè) 0 都不會影響數(shù)字的值。 如果對一個(gè)有符號的負(fù)數(shù)使用 %o 或者 %x 輸出十电,那么結(jié)果就會大相徑庭知押,讀者可以親試。 可以說鹃骂,“有符號正數(shù)的最高位是 0”這個(gè)巧合才使得 %o 和 %x 輸出有符號數(shù)時(shí)不會出錯(cuò)台盯。 再次強(qiáng)調(diào),不管是以 %o畏线、%u静盅、%x 輸出有符號數(shù),還是以 %d 輸出無符號數(shù)寝殴,編譯器都不會報(bào)錯(cuò)蒿叠,只是對內(nèi)存的解釋不同了。%o蚣常、%d市咽、%u、%x 這些格式控制符不會關(guān)心數(shù)字在定義時(shí)到底是有符號的還是無符號的:
●你讓我輸出無符號數(shù)抵蚊,那我在讀取內(nèi)存時(shí)就不區(qū)分符號位和數(shù)值位了魂务,我會把所有的內(nèi)存都看做數(shù)值位曼验;
●你讓我輸出有符號數(shù),那我在讀取內(nèi)存時(shí)會把最高位作為符號位粘姜,把剩下的內(nèi)存作為數(shù)值位。
說得再直接一些熔酷,我管你在定義時(shí)是有符號數(shù)還是無符號數(shù)呢孤紧,我只關(guān)心內(nèi)存,有符號數(shù)也可以按照無符號數(shù)輸出拒秘,無符號數(shù)也可以按照有符號數(shù)輸出号显,至于輸出結(jié)果對不對,那我就不管了躺酒,你自己承擔(dān)風(fēng)險(xiǎn)押蚤。 下面的代碼進(jìn)行了全面的演示:
運(yùn)行結(jié)果:
a=0100, b=0xffffffff, c=720
m=-1, n=-2147483648, p=100
對于絕大多數(shù)初學(xué)者來說,b羹应、m揽碘、n 的輸出結(jié)果看起來非常奇怪,甚至不能理解园匹。按照一般的推理雳刺,b、m裸违、n 這三個(gè)整數(shù)在內(nèi)存中的存儲形式分別是:
當(dāng)以 %x 輸出 b 時(shí)掖桦,結(jié)果應(yīng)該是 0x80000001;當(dāng)以 %hd供汛、%d 輸出 m枪汪、n 時(shí),結(jié)果應(yīng)該分別是 -7fff怔昨、-0雀久。但是實(shí)際的輸出結(jié)果和我們推理的結(jié)果卻大相徑庭,這是為什么呢朱监?
注意岸啡,-7fff 是十六進(jìn)制形式。%d 本來應(yīng)該輸出十進(jìn)制赫编,這里只是為了看起來方便巡蘸,才改為十六進(jìn)制。
其實(shí)這跟整數(shù)在內(nèi)存中的存儲形式以及讀取方式有關(guān)擂送。b 是一個(gè)有符號的負(fù)數(shù)悦荒,它在內(nèi)存中并不是像上圖演示的那樣存儲,而是要經(jīng)過一定的轉(zhuǎn)換才能寫入內(nèi)存嘹吨;m搬味、n 的內(nèi)存雖然沒有錯(cuò)誤,但是當(dāng)以 %d 輸出時(shí),并不是原樣輸出碰纬,而是有一個(gè)逆向的轉(zhuǎn)換過程(和存儲時(shí)的轉(zhuǎn)換過程恰好相反)萍聊。 也就是說,整數(shù)在寫入內(nèi)存之前可能會發(fā)生轉(zhuǎn)換悦析,在讀取時(shí)也可能會發(fā)生轉(zhuǎn)換寿桨,而我們沒有考慮這種轉(zhuǎn)換,所以才會導(dǎo)致推理錯(cuò)誤强戴。那么亭螟,整數(shù)在寫入內(nèi)存前,以及在讀取時(shí)究竟發(fā)生了怎樣的轉(zhuǎn)換呢骑歹?為什么會發(fā)生這種轉(zhuǎn)換呢预烙?我們將在《整數(shù)在內(nèi)存中是如何存儲的,為什么它堪稱天才般的設(shè)計(jì)》一節(jié)中揭開謎底道媚。
3.6 整數(shù)在內(nèi)存中是如何存儲的扁掸,為什么它堪稱天才般的設(shè)計(jì)
加法和減法是計(jì)算機(jī)中最基本的運(yùn)算,計(jì)算機(jī)時(shí)時(shí)刻刻都離不開它們衰琐,所以它們得由硬件直接支持也糊。為了提高加減法的運(yùn)算效率,硬件電路需要設(shè)計(jì)地足夠精簡羡宙。
對于有符號數(shù)狸剃,內(nèi)存要區(qū)分符號位和數(shù)值位,對于人腦來說狗热,很容易辨別钞馁,但是對于計(jì)算機(jī)而言,就要設(shè)計(jì)專門的電路匿刮,這無疑增加了硬件的復(fù)雜性僧凰,增加了計(jì)算的時(shí)間。
要是能把符號位和數(shù)值位等同起來熟丸,讓它們一起參與運(yùn)算训措,不再加以區(qū)分,這樣硬件電路就變得簡單了起來光羞。
另外绩鸣,加法和減法也可以合并為一種運(yùn)算,就是加法運(yùn)算纱兑,因?yàn)闇p去一個(gè)數(shù)相當(dāng)于加上這個(gè)數(shù)的相反數(shù)呀闻,例如:5 - 3等價(jià)于 5 + (-3),10 - (-9)等價(jià)于10+9.
相反數(shù)是指數(shù)值相同潜慎,符號不同的兩個(gè)數(shù)捡多,例如蓖康,10和-10就是一堆相反數(shù),-98和98也是一對相反數(shù)垒手。
如果能夠?qū)崿F(xiàn)上面的兩個(gè)目標(biāo)蒜焊,那么只要設(shè)計(jì)一種簡單的、不用區(qū)分符號位和數(shù)值位的加法電路科贬,就能同時(shí)實(shí)現(xiàn)加法和減法運(yùn)算山涡,并且非常高效。
實(shí)際上唆迁,這兩個(gè)目標(biāo)都已經(jīng)實(shí)現(xiàn)了,真正的計(jì)算機(jī)硬件電路就是如此簡單竞穷。
然而唐责,簡化硬件電路是有代價(jià)的,這個(gè)代價(jià)就是有符號數(shù)在存儲和讀取時(shí)都要進(jìn)行轉(zhuǎn)化瘾带。那么鼠哥,這個(gè)轉(zhuǎn)換過程究竟是怎樣的呢?接下來我們就詳細(xì)地講解一下看政。
首先朴恳,請記住如下幾個(gè)概念。
(1)原碼
將一個(gè)整數(shù)轉(zhuǎn)換成二進(jìn)制形式允蚣,就是其原碼于颖。
例如short a = 6;
a的原碼就是0000 0000 0000 0110;
更改a的值a = -18嚷兔,此時(shí)a的原碼就是1000 0000 0001 0010森渐。
通俗的理解,原碼就是一個(gè)整數(shù)本來的二進(jìn)制形式
(2)反碼
談到反碼冒晰,需要將正數(shù)和負(fù)數(shù)區(qū)別對待同衣,因?yàn)樗鼈兊姆创a不一樣。
對于正數(shù)壶运,它的反碼就是其原碼(原碼和反碼相同)耐齐;
負(fù)數(shù)的反碼就是將原碼中除符號位以外的所有位(數(shù)值位)取反,也就是0變成1,1變成0.蒋情。
例如short a = 6埠况,a的原碼和反碼都是0000 0000 0000 0110;
更改a的值a = -18恕出,此時(shí)a的反碼是 1111 1111 1110 1101.
(3)補(bǔ)碼
正數(shù)和負(fù)數(shù)的補(bǔ)碼也不一樣询枚,也要區(qū)別對待。
對于正數(shù)浙巫,它的補(bǔ)碼就是其原碼(原碼金蜀、反碼刷后、補(bǔ)碼都相同);
負(fù)數(shù)的補(bǔ)碼是其反碼加 1渊抄。
例如short a = 6;尝胆,a 的原碼、反碼护桦、補(bǔ)碼都是0000 0000 0000 0110含衔;
更改 a 的值a = -18;,此時(shí) a 的補(bǔ)碼是1111 1111 1110 1110。
可以認(rèn)為缸血,補(bǔ)碼是在反碼的基礎(chǔ)上打了一個(gè)補(bǔ)丁涂乌,進(jìn)行了一下修正,所以叫“補(bǔ)碼”杭隙。
原碼、反碼因妙、補(bǔ)碼的概念只對負(fù)數(shù)有實(shí)際意義痰憎,對于正數(shù),它們都一樣攀涵。
最后我們總結(jié)一下 6 和 -18 從原碼到補(bǔ)碼的轉(zhuǎn)換過程:
[圖片上傳失敗...(image-3c8f46-1667900143471)]
在計(jì)算機(jī)內(nèi)存中铣耘,整數(shù)一律采用補(bǔ)碼的形式來存儲。這意味著以故,當(dāng)讀取整數(shù)時(shí)還要采用逆向的轉(zhuǎn)換蜗细,也就是將補(bǔ)碼轉(zhuǎn)換為原碼。將補(bǔ)碼轉(zhuǎn)換為原碼也很簡單:先減去 1据德,再將數(shù)值位取反即可鳄乏。
補(bǔ)碼到底是如何簡化電路的
假設(shè) 6 和 18 都是 short 類型的,現(xiàn)在我們要計(jì)算 6 - 18 的結(jié)果棘利,根據(jù)運(yùn)算規(guī)則橱野,它等價(jià)于 6 + (-18)。
如果采用原碼計(jì)算善玫,那么運(yùn)算過程為:
6 - 18 = 6 + (-18) = [0000 0000 0000 0110]原 + [1000 0000 0001 0010]原 = [1000 0000 0001 1000]原 = -24
直接用原碼表示整數(shù)水援,讓符號位也參與運(yùn)算,對于類似上面的減法來說茅郎,結(jié)果顯然是不正確的蜗元。
于是人們開始繼續(xù)探索,不斷試錯(cuò)系冗,后來設(shè)計(jì)出了反碼奕扣。下面就演示了反碼運(yùn)算的過程:
6 - 18 = 6 + (-18) = [0000 0000 0000 0110]反 + [1111 1111 1110 1101]反 = [1111 1111 1111 0011]反 = [1000 0000 0000 1100]原 = -12
這樣一來,計(jì)算結(jié)果就正確了掌敬。
然而惯豆,這樣還不算萬事大吉池磁,我們不妨將減數(shù)和被減數(shù)交換一下位置,也就是計(jì)算 18 - 6 的結(jié)果:
18 - 6 = 18 + (-6) = [0000 0000 0001 0010]反 + [1111 1111 1111 1001]反 = [1 0000 0000 0000 1011]反 = [0000 0000 0000 1011]反 = [0000 0000 0000 1011]原 = 11
按照反碼計(jì)算的結(jié)果是 11楷兽,而真實(shí)的結(jié)果應(yīng)該是 12 才對地熄,它們相差了 1。
加粗的 1 是加法運(yùn)算過程中的進(jìn)位芯杀,它溢出了端考,內(nèi)存容納不了了,所以直接截掉揭厚。
6 - 18 的結(jié)果正確却特,18 - 6 的結(jié)果就不正確,相差 1筛圆。按照反碼來計(jì)算核偿,是不是小數(shù)減去大數(shù)正確,大數(shù)減去小數(shù)就不對了顽染,始終相差 1 呢?我們不妨再看兩個(gè)例子轰绵,分別是 5 - 13 和 13 - 5粉寞。
5 - 13 的運(yùn)算過程為:
5 - 13 = 5 + (-13) = [0000 0000 0000 0101]原 + [1000 0000 0000 1101]原 = [0000 0000 0000 0101]反 + [1111 1111 1111 0010]反 = [1111 1111 1111 0111]反 = [1000 0000 0000 1000]原 = -8
13 - 5 的運(yùn)算過程為:
13 - 5 = 13 + (-5) = [0000 0000 0000 1101]原 + [1000 0000 0000 0101]原 = [0000 0000 0000 1101]反 + [1111 1111 1111 1010]反 = [1 0000 0000 0000 0111]反 = [0000 0000 0000 0111]反 = [0000 0000 0000 0111]原 = 7
這足以證明,剛才的猜想是正確的:小數(shù)減去大數(shù)不會有問題左腔,而大數(shù)減去小數(shù)的就不對了唧垦,結(jié)果始終相差 1。
相差的這個(gè) 1 要進(jìn)行糾正液样,但是又不能影響小數(shù)減去大數(shù)振亮,怎么辦呢?于是人們又絞盡腦汁設(shè)計(jì)出了補(bǔ)碼鞭莽,給反碼打了一個(gè)“補(bǔ)丁”坊秸,終于把相差的 1 給糾正過來了。
下面演示了按照補(bǔ)碼計(jì)算的過程:
6 - 18 = 6 + (-18) = [0000 0000 0000 0110]補(bǔ) + [1111 1111 1110 1110]補(bǔ) = [1111 1111 1111 0100]補(bǔ) = [1111 1111 1111 0011]反 = [1000 0000 0000 1100]原 = -12
18 - 6 = 18 + (-6) = [0000 0000 0001 0010]補(bǔ) + [1111 1111 1111 1010]補(bǔ) = [1 0000 0000 0000 1100]補(bǔ) = [0000 0000 0000 1100]補(bǔ) = [0000 0000 0000 1100]反 = [0000 0000 0000 1100]原 = 12
5 - 13 = 5 + (-13) = [0000 0000 0000 0101]補(bǔ) + [1111 1111 1111 0011]補(bǔ) = [1111 1111 1111 1000]補(bǔ) = [1000 1111 1111 0111]反 = [1000 0000 0000 1000]原 = -8
13 - 5 = 13 + (-5) = [0000 0000 0000 1101]補(bǔ) + [1111 1111 1111 1011]補(bǔ) = [1 0000 0000 0000 1000]補(bǔ) = [0000 0000 0000 1000]補(bǔ) = [0000 0000 0000 1000]反 = [0000 0000 0000 1000]原 = 8
你看澎怒,采用補(bǔ)碼的形式正好把相差的 1 糾正過來褒搔,也沒有影響到小數(shù)減去大數(shù),這個(gè)“補(bǔ)丁”真是巧妙喷面。
小數(shù)減去大數(shù)星瘾,結(jié)果為負(fù)數(shù),之前(負(fù)數(shù)從反碼轉(zhuǎn)換為補(bǔ)碼要加 1)加上的 1惧辈,后來(負(fù)數(shù)從補(bǔ)碼轉(zhuǎn)換為反碼要減 1)還要減去琳状,正好抵消掉,所以不會受影響盒齿。
而大數(shù)減去小數(shù)念逞,結(jié)果為正數(shù)困食,之前(負(fù)數(shù)從反碼轉(zhuǎn)換為補(bǔ)碼要加 1)加上的 1,后來(正數(shù)的補(bǔ)碼和反碼相同肮柜,從補(bǔ)碼轉(zhuǎn)換為反碼不用減 1)就沒有再減去陷舅,不能抵消掉,這就相當(dāng)于給計(jì)算結(jié)果多加了一個(gè) 1审洞。
補(bǔ)碼這種天才般的設(shè)計(jì)莱睁,一舉達(dá)成了本文開頭提到的兩個(gè)目標(biāo),簡化了硬件電路芒澜。
C語言中的小數(shù)(float,double)
小數(shù)分為整數(shù)部分和小數(shù)部分仰剿,它們由點(diǎn)號.分隔,例如 0.0痴晦、75.0南吮、4.023、0.27誊酌、-937.198 -0.27 等都是合法的小數(shù)部凑,這是最常見的小數(shù)形式,我們將它稱為十進(jìn)制形式碧浊。 此外涂邀,小數(shù)也可以采用指數(shù)形式,例如 7.25×102箱锐、0.0368×105比勉、100.22×10-2、-27.36×10-3 等驹止。任何小數(shù)都可以用指數(shù)形式來表示浩聋。 C語言同時(shí)支持以上兩種形式的小數(shù)。但是在書寫時(shí)臊恋,C語言中的指數(shù)形式和數(shù)學(xué)中的指數(shù)形式有所差異衣洁。 C語言中小數(shù)的指數(shù)形式為:aEn 或 aen
a 為尾數(shù)部分,是一個(gè)十進(jìn)制數(shù)抖仅;n 為指數(shù)部分闸与,是一個(gè)十進(jìn)制整數(shù);E或e是固定的字符岸售,用于分割尾數(shù)部分和指數(shù)部分践樱。整個(gè)表達(dá)式等價(jià)于 a×10n。 指數(shù)形式的小數(shù)舉例:
●2.1E5 = 2.1×105凸丸,其中 2.1 是尾數(shù)拷邢,5 是指數(shù)。
●3.7E-2 = 3.7×10-2屎慢,其中 3.7 是尾數(shù)瞭稼,-2 是指數(shù)忽洛。
●0.5E7 = 0.5×107,其中 0.5 是尾數(shù)环肘,7 是指數(shù)欲虚。
C語言中常用的小數(shù)有兩種類型,分別是 float 或 double悔雹;float 稱為單精度浮點(diǎn)型复哆,double 稱為雙精度浮點(diǎn)型。 不像整數(shù)腌零,小數(shù)沒有那么多幺蛾子梯找,小數(shù)的長度是固定的,float 始終占用4個(gè)字節(jié)益涧,double 始終占用8個(gè)字節(jié)锈锤。
小數(shù)的輸出
小數(shù)也可以使用 printf 函數(shù)輸出,包括十進(jìn)制形式和指數(shù)形式闲询,它們對應(yīng)的格式控制符分別是:
●%f 以十進(jìn)制形式輸出 float 類型久免;
●%lf 以十進(jìn)制形式輸出 double 類型;
●%e 以指數(shù)形式輸出 float 類型扭弧,輸出結(jié)果中的 e 小寫妄壶;
●%E 以指數(shù)形式輸出 float 類型,輸出結(jié)果中的 E 大寫寄狼;
●%le 以指數(shù)形式輸出 double 類型,輸出結(jié)果中的 e 小寫氨淌;
●%lE 以指數(shù)形式輸出 double 類型泊愧,輸出結(jié)果中的 E 大寫。
下面的代碼演示了小數(shù)的表示以及輸出:
運(yùn)行結(jié)果:
a=3.020000e-01
b=128.100998
c=123.000000
d=1.126400E+05
e=0.007623
f=1.230024
對代碼的說明: 1) %f 和 %lf 默認(rèn)保留六位小數(shù)盛正,不足六位以 0 補(bǔ)齊删咱,超過六位按四舍五入截?cái)唷?2) 將整數(shù)賦值給 float 變量時(shí)會變成小數(shù)。 3) 以指數(shù)形式輸出小數(shù)時(shí)豪筝,輸出結(jié)果為科學(xué)計(jì)數(shù)法痰滋;也就是說,尾數(shù)部分的取值為:0 ≤ 尾數(shù) < 10续崖。 4) b 的輸出結(jié)果讓人費(fèi)解敲街,才三位小數(shù),為什么不能精確輸出严望,而是輸出一個(gè)近似值呢多艇?這和小數(shù)在內(nèi)存中的存儲形式有關(guān),很多簡單的小數(shù)壓根不能精確存儲像吻,所以也就不能精確輸出峻黍,我們將在下節(jié)《小數(shù)在內(nèi)存中是如何存儲的复隆,揭秘諾貝爾獎(jiǎng)級別的設(shè)計(jì)(長篇神文)》中詳細(xì)講解。 另外姆涩,小數(shù)還有一種更加智能的輸出方式挽拂,就是使用%g。%g 會對比小數(shù)的十進(jìn)制形式和指數(shù)形式样刷,以最短的方式來輸出小數(shù)仑扑,讓輸出結(jié)果更加簡練。所謂最短置鼻,就是輸出結(jié)果占用最少的字符镇饮。 %g 使用示例:
運(yùn)行結(jié)果:
a=1e-05
b=3e+07
c=12.84
d=1.22934
對各個(gè)小數(shù)的分析:
●a 的十進(jìn)制形式是 0.00001,占用七個(gè)字符的位置箕母,a 的指數(shù)形式是 1e-05嘶是,占用五個(gè)字符的位置钙勃,指數(shù)形式較短,所以以指數(shù)的形式輸出希太。
●b 的十進(jìn)制形式是 30000000克饶,占用八個(gè)字符的位置,b 的指數(shù)形式是 3e+07誊辉,占用五個(gè)字符的位置矾湃,指數(shù)形式較短,所以以指數(shù)的形式輸出堕澄。
●c 的十進(jìn)制形式是 12.84邀跃,占用五個(gè)字符的位置,c 的指數(shù)形式是 1.284e+01蛙紫,占用九個(gè)字符的位置拍屑,十進(jìn)制形式較短,所以以十進(jìn)制的形式輸出坑傅。
●d 的十進(jìn)制形式是 1.22934丽涩,占用七個(gè)字符的位置,d 的指數(shù)形式是 1.22934e+00,占用十一個(gè)字符的位置矢渊,十進(jìn)制形式較短继准,所以以十進(jìn)制的形式輸出。
讀者需要注意的兩點(diǎn)是:
●%g 默認(rèn)最多保留六位有效數(shù)字矮男,包括整數(shù)部分和小數(shù)部分移必;%f 和 %e 默認(rèn)保留六位小數(shù),只包括小數(shù)部分毡鉴。
●%g 不會在最后強(qiáng)加 0 來湊夠有效數(shù)字的位數(shù)崔泵,而 %f 和 %e 會在最后強(qiáng)加 0 來湊夠小數(shù)部分的位數(shù)。
總之猪瞬,%g 要以最短的方式來輸出小數(shù)憎瘸,并且小數(shù)部分表現(xiàn)很自然,不會強(qiáng)加零陈瘦,比 %f 和 %e 更有彈性幌甘,這在大部分情況下是符合用戶習(xí)慣的。 除了 %g痊项,還有 %lg锅风、%G、%lG:
●%g 和 %lg 分別用來輸出 float 類型和 double 類型鞍泉,并且當(dāng)以指數(shù)形式輸出時(shí)皱埠,e小寫。
●%G 和 %lG 也分別用來輸出 float 類型和 double 類型咖驮,只是當(dāng)以指數(shù)形式輸出時(shí)边器,E大寫。
數(shù)字的后綴
一個(gè)數(shù)字托修,是有默認(rèn)類型的:對于整數(shù)忘巧,默認(rèn)是 int 類型;對于小數(shù)诀黍,默認(rèn)是 double 類型。 請看下面的例子:
long a = 100;int b = 294;float x = 52.55;double y = 18.6;
100 和 294 這兩個(gè)數(shù)字默認(rèn)都是 int 類型的仗处,將 100 賦值給 a眯勾,必須先從 int 類型轉(zhuǎn)換為 long 類型,而將 294 賦值給 b 就不用轉(zhuǎn)換了婆誓。 52.55 和 18.6 這兩個(gè)數(shù)字默認(rèn)都是 double 類型的吃环,將 52.55 賦值給 x,必須先從 double 類型轉(zhuǎn)換為 float 類型洋幻,而將 18.6 賦值給 y 就不用轉(zhuǎn)換了郁轻。 如果不想讓數(shù)字使用默認(rèn)的類型,那么可以給數(shù)字加上后綴,手動(dòng)指明類型:
●在整數(shù)后面緊跟 l 或者 L(不區(qū)分大小寫)表明該數(shù)字是 long 類型好唯;
●在小數(shù)后面緊跟 f 或者 F(不區(qū)分大小寫)表明該數(shù)字是 float 類型竭沫。
請看下面的代碼:
long a = 100l;int b = 294;short c = 32L; float x = 52.55f;double y = 18.6F;float z = 0.02;
加上后綴,雖然數(shù)字的類型變了骑篙,但這并不意味著該數(shù)字只能賦值給指定的類型蜕提,它仍然能夠賦值給其他的類型,只要進(jìn)行了一下類型轉(zhuǎn)換就可以了靶端。 對于初學(xué)者谎势,很少會用到數(shù)字的后綴,加不加往往沒有什么區(qū)別杨名,也不影響實(shí)際編程脏榆,但是既然學(xué)了C語言,還是要知道這個(gè)知識點(diǎn)的台谍,萬一看到別人的代碼這么用了须喂,而你卻不明白怎么回事,那就尷尬了典唇。
關(guān)于數(shù)據(jù)類型的轉(zhuǎn)換镊折,我們將在《C語言數(shù)據(jù)類型轉(zhuǎn)換》一節(jié)中深入探討。
小數(shù)和整數(shù)相互賦值
在C語言中介衔,整數(shù)和小數(shù)之間可以相互賦值:
●將一個(gè)整數(shù)賦值給小數(shù)類型恨胚,在小數(shù)點(diǎn)后面加 0 就可以,加幾個(gè)都無所謂炎咖。
●將一個(gè)小數(shù)賦值給整數(shù)類型赃泡,就得把小數(shù)部分丟掉,只能取整數(shù)部分乘盼,這會改變數(shù)字本來的值升熊。注意是直接丟掉小數(shù)部分,而不是按照四舍五入取近似值绸栅。
請看下面的代碼:
include <stdio.h>int main(){ float f = 251; int w = 19.427; int x = 92.78; int y = 0.52; int z = -87.27; printf("f = %f, w = %d, x = %d, y = %d, z = %d\n", f, w, x, y, z); return 0;}
運(yùn)行結(jié)果: f = 251.000000, w = 19, x = 92, y = 0, z = -87
由于將小數(shù)賦值給整數(shù)類型時(shí)會“失真”级野,所以編譯器一般會給出警告,讓大家引起注意粹胯。