[TOC] 第7章 基本類型程序員
請別搞錯:計算機處理的是數而不是符號。咱們用對行爲的算術化程度來衡量咱們的理解力(和控制力)。編程
到目前爲止,本書只使用了C語言的兩種基本(內置的)類型:int和float。(咱們還見到過_Bool
,那是C99中的一種基本類型。)本章講述其他的基本類型,並從整體上討論了與類型有關的重要問題。7.1節展現整數類型的取值範圍,包括長整型、短整型和無符號整型。7.2節介紹double類型和long double類型,這些類型提供了更大的取值範圍和比float類型更高的精度。7.3節討論char(字符)類型,這種類型將用於字符數據的處理。7.4節解決重要的類型轉換問題,即把一種類型的值轉換成另一種類型的等價值。7.5節展現利用typedef定義新類型名的方法。最後,7.6節描述sizeof運算符,這種運算符用來計算一種類型須要的存儲空間大小。函數
C語言支持兩種根本不一樣的數值類型:整數類型(也稱整型)和浮點類型(也稱浮點型)。整數類型的值是整數,而浮點類型的值則可能還有小數部分。整數類型又分爲兩大類:有符號型和無符號型。spa
有符號整數和無符號整數code
有符號整數若是爲正數或零,那麼最左邊的位(符號位)爲0;若是是負數,則符號位爲1。所以,最大的16位整數的二進制表示形式是0111111111111111,對應的值是32 767(即$2^{15}-1$)。而最大的32位整數是01111111111111111111111111111111,對應的數值是2 147 483 647(即$2^{31}-1$)。不帶符號位的整數(最左邊的位是數值的一部分)的整數稱爲無符號整數。最大的16位無符號整數是65 535(即$2^{16}-1$),而最大的32位無符號整數是4 294 967 295(即$2^{32}-1$)。ci
默認狀況下,C語言中的整數變量都是有符號的,也就是說最左位保留爲符號位。若要告訴編譯器變量沒有符號位,須要把它聲明成unsigned類型。無符號整數主要用於系統編程和底層與機器相關的應用。第20章將討論無符號整數的常見應用,在此以前,咱們一般迴避無符號整數。開發
C語言的整數類型有不一樣的尺寸。int類型一般爲32位,但在老得CPU上多是16位。有些程序所需的數很大,沒法以int類型存儲,因此C語言還提供了長整型。某些時候,爲了節省空間,咱們會指示編譯器以比正常存儲小的空間來存儲一些數,稱這樣的數爲短整型。cmd
爲了使構造的整數類型正好知足要求,能夠指明變量是long類型或short類型,singed類型或unsigned類型,甚至能夠把說明符組合起來(如long unsigned int)。然而,實際上只有下列6種組合能夠產生不一樣的類型:編譯器
short int unsigned short int int unsigned int long int unsigned long int
其餘組合都是上述某種類型的同義詞。(例如,除非額外說明,不然全部整數都是有符號的。所以,long signed int和long int是同樣的類型。)另外,說明符的順序沒什麼影響,因此unsigned short int和short unsigned int是同樣的。it
C語言容許經過省略單詞int來縮寫整數類型的名字。例如,unsigned short int能夠縮寫爲unsigned short,而long int則能夠縮寫爲long。C程序員常常會省略int;一些新出現的基於C的語言(包括Java)甚至不容許程序員使用short int或long int這樣的名字,而必須寫成short或long。基於這些緣由,本書在單詞int無關緊要的狀況下一般將其省略。
6種整數類型的每一種所表示的取值範圍都會根據機器的不一樣而不一樣,可是有兩條全部編譯器都必須遵照的原則。首先,C標準要求short int、int和long int中的每一種類型都要覆蓋一個肯定的最小取值範圍(詳見23.2節)。其次,標準要求int類型不能比short int類型短,long int類型不能比int類型短。可是,short int類型的取值範圍有可能和int類型的範圍是同樣的,int類型的取值範圍也能夠和long int的同樣。
表7-1說明了在16位機上整數類型一般的取值範圍,注意short int和int有相同的取值範圍。
表7-1 16位機的整數類型
類型 | 最小值 | 最大值 |
---|---|---|
short int | -32 768 | 32 767 |
unsigned short int | 0 | 65 535 |
int | -32 768 | 32 767 |
unsigned int | 0 | 65 535 |
long int | -2 147 483 648 | 2 147 483 647 |
unsigned long int | 0 | 4 294 967 295 |
表7-2說明了32位機上整數類型一般的取值範圍,這裏的int和long int有着相同的取值範圍。
表7-2 32位機的整數類型
類型 | 最小值 | 最大值 |
---|---|---|
short int | -32 768 | 32 767 |
unsigned short int | 0 | 65 535 |
int | -2 147 483 648 | 2 147 483 648 |
unsigned int | 0 | 4 294 967 295 |
long int | -2 147 483 648 | 2 147 483 647 |
unsigned long int | 0 | 4 294 967 295 |
最近64位的CPU逐漸流行起來了。表7-3給出了64位機上(尤爲是在UNIX系統下)整數類型常見的取值範圍。
類型 | 最小值 | 最大值 |
---|---|---|
short int | -32 768 | 32 767 |
unsigned short int | 0 | 65 535 |
int | -2 147 483 648 | 2 147 483 648 |
unsigned int | 0 | 4 294 967 295 |
long int | -9 223 372 036 854 775 808 | 9 223 372 036 854 775 808 |
unsigned long int | 0 | 18 446 744 073 709 551 615 |
再強調一下,表7-一、表7-2和表7-3中給出的取值範圍不是C標準強制的,會隨着編譯器的不一樣而不一樣。對於特定的實現,肯定整數類型範圍的一種方法是檢查<limits.h>頭(23.2節)。該頭是標準庫的一部分,其中定義了表示每種整數類型的最大值和最小值的宏。
C99提供了兩個額外的標準整數類型:long long int和unsigned long long int。增長這兩種整數類型有兩個緣由,一是爲了知足日益增加的對超大型整數的需求,而是爲了適應支持64位運算的新處理器的能力。這兩個long long類型要求至少64位寬,因此long long int類型值得範圍一般爲$-2^{63}$(-9 223 372 036 854 775 808)到$2^{63}-1$(9 223 372 036 854 775 807),而unsigned long long int類型值得範圍一般爲0到$2^{64}-1$(18 446 744 073 709 551 615)。
C99中把short int、int、long int和long long int類型(以及signed char類型(7.3節))稱爲標準有符號整型,而把unsigned short int、unsigned int、unsigned long int和unsigned long long int類型(以及unsigned char類型(7.3節)和_Bool
類型(5.2節))稱爲標準無符號整型。
除了標準的整數類型之外,C99標準還容許在具體實現時定義擴展的數類整型(包括有符號和無符號的)。例如,編譯器能夠提供有符號和無符號的128位整數類型。
如今把注意力轉向常量——在程序中以文本形式出現的數,而不是讀、寫或計算出來的數。C語言容許用十進制(基數爲10)、八進制(基數爲8)和十六進制(基數爲16)形式書寫整數常量。
八進制數和十六進制數
八進制數是用數字0~7書寫的。八進制數的每一位表示一個8的冪(這就如同十進制數的每一位表示10的冪同樣)。所以,八進制的數237表示成十進制數就是$28^2+38^1+7*8^0=128+24+7=159$。
十六進制數是用數字0~9加上字母A ~ F表示10~15的數。十六進制數的每一位表示一個16的冪,十六進制數1AF的十進制數值是$116^2+1016^1+15*16^0=256+160+15=431$。
請記住八進制和十六進制只是書寫數的方式,它們不會對數的實際存儲方式產生影響。(整數都是以二進制形式存儲的,跟表示方式無關。)任什麼時候候均可以從一種書寫方式切換到另外一種書寫方式,甚至能夠混合使用:10+015+0x20的值爲55(十進制)。八進制和十六進制更適用於底層程序的編寫,本書直到第20章纔會較多地用到它們。
十進制整數常量的類型一般爲int,但若是常量的值大得沒法存儲在int型中,就用long int 類型。若是出現long int 不夠用的罕見狀況,編譯器會用unsigned long int 做最後的嘗試。肯定八進制和十六進制常量的規則略有不一樣:編譯器會依次嘗試int、unsigned int、long int和unsigned long int類型,直至找到能表示該常量的類型。
爲了強制編譯器把常量做爲長整數來處理,只需在後邊加上一個字母L(或l):
15L 0377L 0x7fffL
爲了指明是無符號常量,能夠在常量後邊加上字母U(或u):
15U 0377U 0x7fffU
L和U能夠結合使用,以代表常量既是長整型又是無符號的:0xffffffffUL
。(字母L、U的順序和大小寫無所謂。)
在C99中,以LL或ll(兩個字母大小寫要一致)結尾的整數常量是long long int型的。若是在LL或ll的前面或後面增長字母U(或u),則該整數常量爲unsigned long long int型。
C99肯定整數常量類型的規則與C89有些不一樣。對於沒有後綴(U、u、L、l、LL、ll)的十進制常量,其類型是int、long int或long long int中能表示該值的「最小」類型。對於八進制或者十六進制常量,可能的類型順序爲int、unsigned int、long int、unsigned long int、long long int和unsigned long long int。常量後面的任何後綴都會改變可能類型的列表。例如,以U(或u)結尾的常量類型必定是unsigned int、unsigned long int 和unsigned long long int中的一種,以L(或l)結尾的十進制常量類型必定是long int或long long int中的一種。若是常量的數值過大以致於不能用標準的整數類型表示,則可使用擴展的整數類型。
對整數執行算術運算時,其結果有可能由於太大而沒法表示。例如,對兩個int值進行算術運算時,結果必須仍然能用int類型來表示;不然(表示結果所需的數位太多)就會發生溢出。
整數溢出時的行爲要根據操做數是有符號型仍是無符號型來肯定。有符號整數運算中發生溢出時,程序的行爲是未定義的。回顧4.4節的介紹可知,未定義行爲的結果是不肯定的。最可能的狀況是,僅僅是運算的結果出錯了,但程序也有可能崩潰,或出現其餘意想不到的情況。
無符號整數運算過程當中發生溢出時,結果是有定義的:正確答案對$2^n$取模,其中n是用於存儲結果的位數。例如,若是對無符號的16位數65 535加1,其結果能夠保證爲0。
假設有一個程序由於其中一個int變量發生了「溢出」而沒法工做。咱們的第一反應是把變量的類型從int變爲long int。但僅僅這樣作事不夠的,咱們還必須檢查數據類型的改變對程序其餘部分的影響,尤爲是須要檢查該變量是否用在printf函數或scanf函數的調用中。若是用了,須要改變調用中的格式串,由於%d只適用於int類型。
讀寫無符號整數、短整數和長整數須要一些新的轉換說明符。
unsigned int u; scanf("%u", &u);/* reads u in base 10 */ printf("%u", u);/* writes u in base 10 */ scanf("%o", &u);/* reads u in base 8 */ printf("%o", u);/* writes u in base 8 */ scanf("%x", &u);/* reads u in base 16 */ printf("%x", u);/* writes u in base 16 */
程序:數列求和(改進版)
6.1節編寫了一個程序對用戶輸入的整數數列求和。該程序的一個問題就是所求出的和(或其中某個輸入數)可能會超出int型變量容許的最大值。若是程序運行在整數長度爲16位的機器上,可能會發生下面的狀況:
This program sums a series of integers. Enter integers (0 to terminate):10000 20000 30000 0 The sum is: -5536
求和的結果應該爲60 000,但這個值不在int型變量表示的範圍內,因此出現了溢出。當有符號整數發生溢出時,結果是未定義是未定義的,在本例中咱們獲得了一個毫無心義的結果。爲了改進這個程序,能夠把變量改爲long型。
/** * Sums a series of numbers (using long int variables) */ #include <stdio.h> int main() { long n, sum = 0; printf("This program sums a series of integers.\n"); printf("Enter integers (0 to terminate): "); scanf("%ld", &n); while (n != 0) { sum += n; printf("The sum is: %ld\n", sum); scanf("%ld", &n); if (n < 0) { printf("Length Overflow!!!"); break; } } return 0; }
這種改變很是簡單:將n和sum聲明爲long型變量而不是int型變量,而後把scanf和printf函數中的轉換說明由%d改成%ld。
整數類型並不適用於全部應用。有些時候須要變量能存儲帶小數點的數,或者能存儲極大數或極小數。這類數能夠用浮點(因小數點是「浮動的」而得名)格式進行存儲。C語言提供了3種浮點類型,對應三種不一樣的浮點格式。
當精度要求不嚴格時,(例如,計算帶一位小數的溫度),float類型是很適合的類型。double提供更高的進度,對絕大多數程序來講都夠用了。long double支持極高精度的要求,不多會用到。
C標準沒有說明float、double和long double類型提供的精度究竟是多少,由於不一樣的計算機能夠用不一樣方法存儲浮點數。大多數現代計算機都遵循IEEE 754標準(即IEC 60559)的規範,因此這裏也用它做爲一個示例。
IEEE浮點標準
由IEEE開發的IEEE標準提供了兩種主要的浮點數格式:單精度(32位)和雙精度(64位)。數值以科學計數法的形式存儲,每個數都由三部分組成:符號、指數和小數。指數部分的位數說明了數值的可能大小程度,而小數部分的位數說明了精度。單精度格式中,指數長度爲8位,而小數部分佔了23位。所以,單精度數能夠表示的最大值大約是$3.40*10^{38}$,其中精度是6個十進制數字。
IEEE標準還描述了另外兩種格式:單擴展精度和雙擴展精度。標準沒有指明這些格式中的位數,但要求單擴展精度類型至少爲43位,而雙精度類型至少爲79位。爲了得到更多有關IEEE標準和浮點算術的信息,能夠參閱David Goldberg在1991年3月發佈的「What every computer scientist should know about floating-point arithmetic」(ACM Computing Surveys, vol, 23, no.1: 5-48 )。
表7-4給出了根據IEEE標準實現時浮點類型的特徵。(表中給出了規範化的最小正值,非規範化的數(23.4節)能夠更小。)long double 類型沒有顯示在此表中,由於它的長度隨着機器的不一樣而變化,而最多見的大小是80位和128位。
類型 | 最小正值 | 最大值 | 精度 |
---|---|---|---|
float | $1.17549*10^{-38}$ | $3.40282*10^{38}$ | 6個數字 |
double | $2.225 07*10^{-308}$ | $1.79769*10^{308}$ | 15個數字 |
在不遵循IEEE標準的計算機上,表7-4是無效的。事實上,在一些機器上,float能夠有和double相同的數值集合,或者double能夠有和long double相同的數值集合。能夠在頭<float.h>
(23.1節)中找到定義浮點類型特徵的宏。
在C99中,浮點類型分爲兩種:一種是實浮點類型,包括float、double和long double類型;另外一種是C99新增的複數類型(27.3節,包括float _Complex、double _Complex和long double _Complex)。