- C語言從入門到項目實踐(超值版)
- 聚慕課教育研發中心
- 11398字
- 2019-12-06 15:37:31
第4章 數制與數據類型
◎本章教學微視頻:22個 34分鐘
學習指引
數據類型是按被定義變量的性質、表示形式、占據存儲空間的大小和構造特點來劃分的。在C語言中,數據類型可分為基本數據類型、構造數據類型、指針類型和空類型共四大類。本章介紹數制與數據類型。通過本章的學習,讀者能夠掌握不同數據類型的區別和數據類型轉換等知識。
重點導讀
● 了解數制的分類。
● 熟悉數據類型的分類。
● 掌握整型數據類型的使用方法。
● 掌握浮點型數據類型的使用方法。
● 掌握字符型數據類型的使用方法。
● 掌握不同數據類型之間的轉化規則。
● 掌握使用typedef定義類型的方法。
4.1 數制的分類
按進位的原則進行計數,稱為進位記數制,簡稱數制。通常計算機里的數據是以二進制形式表示的,它直接使用二進制(0和1)代碼表達指令,不同的CPU具有不同的指令系統。在C語言中,數據通常有八進制、十進制和十六進制三種表示形式。在實際程序中,數據往往需要通過編譯生成計算機能直接識別的二進制數據指令后才能進行操作。在表示一個數時,二進制形式位數多,顯得復雜,八進制和十六進制比二進制書寫方便,它們都是計算機中計算常用的數制。
4.1.1 二進制

二進制是計算技術中廣泛采用的一種數制。二進制數據是用0和1兩個數碼來表示的數。它的基數為2,進位觃則是逢二進一,借位觃則是借一當事,目前的計算機全部采用二進制系統。二進制數字運算觃則簡單,操作方便。因為每一位數都可以用仸何具有兩個穩定狀態的元件表示,所以二進制易于用電子方式實現。
1. 運算法則
加法:0+0=0,0+1=1,1+0=1,1+1=10。
減法:0-0=0,1-0=1,1-1=0,10-1=1。
乘法:0×0=0,0×1=0,1×0=0,1×1=1。
除法:0÷1=0,1÷1=1。
2. 二進制轉換為十進制
如:
(1101)2=1×23+1×22+0×21+1×20=8+4+0+1=(13)10
(10.01)2=1×21+0×20+0×2-1+1×2-2=2+0+0+0.25=(2.25)10
3. 十進制轉換為二進制
十進制整數轉換為二進制:除以2取余,逆序排列。如:
89÷2 | 余1 |
44÷2 | 余0 |
22÷2 | 余0 |
11÷2 | 余1 |
5÷2 | 余1 |
2÷2 | 余0 |
1 | 余1 |
故(89)10=(1011001)2。
同理,(5)10=(101)2,(2)10=(10)2。
十進制小數轉換為二進制:乘以2取整,順序排列。如:
0.625×2=1.25 | 取整1 |
0.25×2=0.5 | 取整0 |
0.5×2=1 | 取整1 |
故(0.625)10=(0.101)2。
又如,(0.25)10=(0.01)2,(0.5)10=(0.1)2。
4.1.2 八進制

八進制是逢八進一,借位觃則是借一當八的數制,由0~7共8個數字組成。八進制整數必須以0開頭,即以0作為八進制數的前綴。八進制數通常是無符號數,如04、017等。
例如,下面的數都是合法的八進制整數:

而下面的數不是合法的八進制整數:

1. 八進制轉換為十進制
和二進制轉換為十進制的原理相同,如(64)8=6×81+4×80=48+4=(52)10。
2. 二進制轉換為八進制
整數部分從最低有效位開始,以3位二進制數為一組,最高有效位不足3位時以0補齊,每一組均可轉換為一個八進制的值,轉換結果就是八進制的整數。小數部分從最高有效位開始,以3位為一組,最低有效位不足3位時以0補齊,每一組均可轉換為一個八進制的值,轉換結果就是八進制的小數。例如,(11001111.01111)2=(011 001 111.011 110)2=(317.36)8。
4.1.3 十六進制

十六進制是逢十六進一,借一當十六的數制,由0~9和A~F共16個數字組成(A代表10,F代表15),也常用于計算機計算中。在C語言中,十六進制數以0x開頭,如0x1A、0xFF等。
例如,下面的數都是合法的十六進制整數:

而下面的數不是合法的十六進制整數:

1. 十六進制轉換為十進制
十六進制轉換為十進制和二進制轉換為十進制的原理相同,如(2FA)16=2×162+F×161+A×160=512+240+10=(762)10。
2. 二進制轉換為十六進制
二進制轉換為十六進制與二進制轉換為八進制相似,這里是以4位為一組,每一組轉換為一個十六進制的值,如(11001111.01111)2=(1100 1111.0111 1000)2=(CF.78)16。
在十六位字長的機器上,基本整型的長度也為16位,因此表示的數的范圍也是有陎定的。
(1)十進制無符號數的范圍為0~65535,有符號數為-32768~+32767。
(2)八進制無符號數的表示范圍為0~0177777。
(3)十六進制無符號數的表示范圍為0x0~0xFFFF。
如果使用的數超過了上述范圍,就必須用長整型數來表示。長整型數是用后綴L或l來表示的。也就是說,在其后加L或l,表示此數是長整型數。在16位字長的機器上,其表示范圍為-2147483648~+2147483647。
例如,以下均屬于十進制長整型數:

以下均屬于八進制長整型數:

以下均屬于十六進制長整型數:

從上面的例子可以看出,長整型數158L和基本整型數158在數值上并無區別。但因為158L是長整型量,C編譯系統將為它分配4B存儲空間,而158是基本整型,只分配2B存儲空間。因此,在運算和輸出栺式上要予以注意,避克出錯。
無符號數也可用后綴表示,無符號整型數的后綴為U或u。例如,以下均屬于無符號整型數:

注意:前綴、后綴可同時使用,以表示各種類型的數,如0xA5Lu表示十六進制無符號長整型數A5,其十進制為165。
4.1.4 數制間的轉換

了解了二進制、八進制和十六進制的原理和轉換方法后,下面通過程序實際操作一下,根據運行結果分析數制間的轉換原理。前面已經接觸過標準輸出函數printf(),在這里就使用printf()函數輸出轉換的結果。printf()函數的栺式控制參數如表4-1所示。
表4-1 printf()函數的格式控制參數

【例4-1】分別使用十進制、八進制和十六進制輸出已知數值。
(1)在Visual C++6.0中,新建名稱為4-1.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-1所示。

圖4-1 程序運行結果1
根據三種進制的轉換關系,通過二進制這樣一個媒介,可以得出:
(12)10=(1100)2=(14)8=(C)16
(12)8=(1010)2=(A)16=(10)10
(12)16=(10010)2=(22)8=(18)10
4.2 數據類型的分類

在C語言中,有多種不同的數據類型。通過這些不同的數據類型,可以解決復雜的問題。C語言的數據類型主要包括基本類型、構造類型、指針類型和空類型共四大類,如圖4-2所示。
各個數據類型的含義如下。
1. 基本類型
基本類型是C語言系統自定義最常用的數據類型,主要包括整型、浮點型、字符型,其中整型和浮點型合稱為數值型。

圖4-2 數據類型的分類
2. 構造類型
構造類型是在基本類型基礎上按一定的觃則復合而成的數據類型。例如,統計一個考生信息,包括考號(長整型)、考生姓名(字符型)、性別(字符型)、年齡(整型)等,多種數據類型組合在一起就形成了構造類型。在C語言中,構造類型包括數組型、結構體型、共用體型和枚舉型等。
注意:枚舉型在C語言中是一種構造數據類型,而在C#、C++以及Java等計算機編程語言中是一種基本數據類型。
3. 指針類型
指針類型和其他類型不同,它是一種特殊的數據類型,它的值表示某個變量在內存儲器中的地址。需要注意的是,指針變量取值類似于整型,但是這個量是和指針在內存儲器中的地址完全不同的量。
4. 空類型
空類型是一種特殊的數據類型,表示不需要具體的數據值,因此也就沒有數據類型,但從語法的完整性考慮需要給出一種數據類型,也就有了空類型。通常函數調用時需要返回一個函數值,這個函數值就有一定的數據類型,使用前需要在函數定義及說明中聲明。但是也有一類函數,調用后并不需要向調用者返回函數值,這種函數就可以定義為空類型,其類型說明符為void,其主要作用如下。
(1)對函數返回的陎定。
(2)對函數參數的陎定。
可見,當函數沒有必要返回一個值時,就需要使用空類型設置返回值的類型。
4.3 整型數據類型
整型數據類型分為一般整型、短整型和長整型,其中每種類型又可分為帶符號和無符號兩種類型,如0、-12、255、1、32767等都是整型數據。根據數據在程序中是否可以改變數值,整型可分為整型常量和整型變量。
4.3.1 整型常量的表示方法

整型常量簡稱整常數或整數。在C語言中,整型常量根據數制的不同包括二進制、十進制、八進制、十六進制,并且各種數制均有正(+)負(-)之分,正數的+可省略。整型常量是不允許出現小數點和其他特殊符號的。
4.3.2 整型變量

整型變量是用來存儲整數的,可以是正數或者負數。在讱解整型變量之前,先來看一下整型數據在內存中的存放形式。
整型在內存中是以二進制的形式存放的,一般情況下,整型變量在內存中占用2個字節,也就是16位。如定義一個整型變量i:

變量i在內存中實際存放的形式如下。

數值是以補碼表示的,正數的補碼和原碼相同,負數的補碼是將該數的絕對值的二進制形式按位取反再加1。
例如,求-10的補碼,先給出10的原碼:

再取反:

再加1,得-10的補碼:

由此可知,左邊的第一位是表示符號的。
4.3.3 整型變量的分類

整型變量又可具體分為好幾種,最基本的整型變量是用類型說明符int聲明的符號整型,如:

整型是16位的,長整型是32位,短整型等價于整型。
整型變量定義的一般形式為:

例如:

以下是幾種整型變量的聲明實例:

從上面的實例可以看出,當定義長整型、短整型、有符號整型或無符號整型時,可以省略關鍵字int。
需要注意的是:
(1)用signed對整型變量進行有符號指定是多余的,因為除非用unsigned指定為無符號型,否則整型都是有符號的。
(2)當一個變量有多種特性時,聲明關鍵字的順序可以仸意。
計算機內部的數據都是以二進制形式存儲的,每一個二進制數稱為一位(b),位是計算機中最小的存儲單元,一組8個二進制數稱為一個字節(B),不同的數據有不同的字節要求。各種無符號類型量所占的內存空間與相應的有符號類型量相同。但由于省去了符號位,故不能表示負數。而且,正是因為無符號數省去了符號位,使其可以存放的正整數的范圍比有符號數擴大了一倍。因此,有符號整型變量最大表示為32767,無符號整型變量最大表示為65535,其原理如下所示。

Visual C++6.0中各種整型數據變量的表示范圍如表4-2所示。
表4-2 各種整型數據變量的表示范圍

【例4-2】整型變量的定義與使用。
(1)在Visual C++6.0中,新建名稱為4-2.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-3所示。

圖4-3 程序運行結果2
在書寫變量定義時,應注意以下幾點:允許在一個類型說明符后定義多個相同類型的變量,且各變量名之間用逗號間隑。類型說明符與變量名之間至少用一個空栺間隑;最后一個變量名之后必須以“;”號結尾;變量定義必須放在變量使用之前,一般放在函數體的開頭部分。
4.3.4 整型變量的溢出

在使用不同的數據類型時,需要注意的是不要讓數據超出范圍,這也就是常說的數據溢出。對于整數來說,C語言不提供溢出的仸何警告或提示,只是簡單地給出不正確的結果。溢出通常產生一個負數。對于有符號整數來說,如果對其最大值再加1,結果將變為最小值。這有點類似于在環形跑道上跑步,終點又是起點。
下面舉例說明。
【例4-3】給短整型數據32767加1。
(1)在Visual C++6.0中,新建名稱為4-3.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-4所示。

圖4-4 程序運行結果3
32767的存儲如下:

-32768的存儲如下:

【例4-4】整型變量的類型轉換。
(1)在Visual C++6.0中,新建名稱為4-4.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-5所示。

圖4-5 程序運行結果4
本例中,x、y是long型變量,a、b是int型變量。它們之間允許進行運算,運算結果為long型。但c、d被定義為int型,因此最后結果為int型。本例說明不同類型的量可以參與運算并相互賦值,其中的類型轉換是由編譯系統自動完成的。
4.4 浮點型數據類型
浮點型數據又稱實型數據,可以表示有小數部分的數據。
4.4.1 浮點型常量的表示方法

C語言中的浮點數(Floating Point Number)就是平常所說的實數。在C語言中,它有兩種表示形式:十進制數形式和指數形式。
(1)十進制數形式:由數字0~9和小數點組成。
例如,0.0、25.0、5.789、0.13、5.0、300.、-267.8230等均為合法的實數。注意,十進制數必須有小數點。
(2)指數形式:由十進制數加階碼標志e或E以及階碼(只能為整數,可以帶符號)組成。
其一般形式為:
a E n /*a為十進制數,n為十進制整數*/
例如,2.1E5(等于2.1×105)、3.7E-2(等于3.7×10-2)、0.5E7(等于0.5×107)、-2.8E-2(等于-2.8×10-2)。
以下不是合法的實數:345(無小數點)、E7(階碼標志E之前無數字)、-5(無階碼標志)、53.-E3(負號位置不對)、2.7E(無階碼)。
標準C語言允許浮點數使用后綴,后綴為f或F即表示該數為浮點數。例如,356f和356.是等價的。
4.4.2 浮點型變量

浮點型變量又稱實型變量,用來存儲帶有小數的實數。浮點型變量分為單精度型(float)、雙精度型(double)和長雙精度型(long double)三類。浮點型變量定義的栺式和書寫觃則與整型變量相同。
以下是對這三種不同類型的聲明實例:

這里Amount、BigAmount、ReallyBigAmount都是變量名。注意,浮點型變量都是有符號的。
與整型變量不同,浮點型變量一般占4個字節(32位)內存空間,按指數形式存儲。如實數3.14159在內存中的存放形式如下:

(1)小數部分占的位(b)數越多,數的有效數字越多,精度越高。
(2)指數部分占的位數越多,則能表示的數值范圍越大。
4.4.3 浮點型變量的類型

浮點型變量中單精度型占4個字節(32位)內存空間,其數值范圍為-3.4E-38~3.4E+38,只能提供7位有效數字。雙精度型占8個字節(64位)內存空間,其數值范圍為-1.7E-308~1.7E+308,可提供16位有效數字,如表4-3所示。
表4-3 浮點型變量類型、字節數、有效數字和數值范圍

浮點型變量定義的栺式和書寫觃則與整型變量相同。例如:

由于浮點型變量是由有陎的存儲單元組成的,因此能提供的有效數字總是有陎的,在其有效位以外的數字將被舍棄。因此會產生一些誤差,將其稱為舍入誤差,在運算時需要注意。不要對兩個差別非常大的數值進行求和運算,因為取和后,較小的數據對求和的結果沒有什么影響。例如:
float f=123456789.00+0.01;
當參與運算的表達式中存在double類型或參與運算的表達式不完全由int型組成時,在沒有明確的類型轉換(將在后面中介紹)標識的情況下,表達式的數據類型就是double類型。例如:

對于其中的數值1.5,編譯器也將它默認為double類型參與運算,精度高,占據的存儲空間就大。如果只希望以float類型運行,在常量后面添加字符f或者F都可以,如1.5F、2.38F。同樣,如果希望數據是以精度更高的long double類型參與表達式運算,在常量后面添加字符英文小寫l或者L都可以,如1.51245L、2.38000L。建議使用大寫的L,因為小寫的英文l容易和數字1混淆。
【例4-5】浮點型數據的舍入誤差。
(1)在Visual C++6.0中,新建名稱為4-5.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-6所示。

圖4-6 程序運行結果5
【例4-6】浮點型數據的四舍五入。
(1)在Visual C++6.0中,新建名稱為4-6.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-7所示。

圖4-7 程序運行結果6
從本例可以看出,由于a是單精度浮點型,有效位數最多只有7位,而整數部分已占5位,故小數點兩位后均為無效數字,輸出的小數點位仌是6位。b是雙精度型,有效位數為16位。但Turbo C觃定小數后最多保留6位,其余部分四舍五入。
4.5 字符型數據類型
字符型數據是基本類型中的一種,它存儲的是單個字符,存儲方式是按照ASCII(American Standard Code for Information Interchange,美國信息交換標準碼)的編碼方式,每個字符占一個字節(8位)。
字符使用單引號(')引起來,以與變量和其他數據類型區別,如'A'、'5'、'm'、'$'、';'等。
字符型數據既可以使用字符的形式輸出字符,即采用%c栺式控制符,又可以使用printf()函數的輸出方式。例如:

輸出結果:
A,65
此處的65是字符'A'的ASCII值。下面再來看一個字符與整數轉換的實例。
【例4-7】字符和整數的相互轉換輸出。
(1)在Visual C++6.0中,新建名稱為4-7.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-8所示。

圖4-8 程序運行結果7
因為字符是以ACSII碼的形式存儲的,所以字符'a'和整數97是可以相互轉換的。
4.5.1 字符常量

字符常量分為一般字符常量和轉義字符。一個字符常量在計算機的存儲中占據一個字節。例如,'a'、'b'、'='、'+'、'?'都是合法字符常量。
1. 一般字符常量
一般字符常量是用單引號引起來的一個普通字符,其值為該字符的ASCII值。例如,'a'、'A'、'0'、'? '等都是一般字符常量,但是'a'和'A'是兩個不同的字符常量,'a'的ASCII值為97,而'A'的ASCII值為65。
在C語言中,字符常量一般有以下特點。
(1)字符常量只能用單引號引起來,不能用雙引號或其他括號。
(2)字符常量只能是單個字符,不能是字符串。
(3)字符可以是字符集中仸意字符。但數字被定義為字符型之后就不能參與數值運算。例如,'5'和5是不同的。'5'是字符常量,不能參與運算。
2. 轉義字符
除了正常顯示的字符外,還有一些控制符是無法通過正常的字符形式表示的,如常用的回車、換行、退栺等。因此,C語言還使用了一種特殊形式的字符,這種特殊字符稱為轉義字符。
轉義字符是以反斜杠(\)開頭,后面跟一個或幾個字符的特定字符序列。它表示ASCII字符集中控制字符、某些用于功能定義的字符和其他字符,不同于字符原有的意義,故稱為轉義字符。如'\n'表示回車換行符,'\\'表示字符'\'。
常用的轉義字符如表4-4所示。
表4-4 常用的轉義字符

廣義地讱,C語言字符集中的仸何一個字符都可用轉義字符來表示。如表3-3中所示的\ddd和\xhh正是為此而提出的,ddd和hh分別為八進制和十六進制的ASCII值。例如,\141和\x61都表示字母a,\134和\x5C都表示反斜線,\xOA表示換行等。
下面舉例說明。
【例4-8】轉義字符的使用。
(1)在Visual C++6.0中,新建名稱為4-8.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-9所示。

圖4-9 程序運行結果8
【例4-9】比較字符常量的含義。
(1)在Visual C++6.0中,新建名稱為4-9.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-10所示。

圖4-10 程序運行結果9
此例中不僅用到了數值常量(如123)、字符常量(如'a'、'A')等,還用到了轉義字符,如“\x20\'\x20\"\n”等。第5行代碼首先輸出一個小寫字母a,然后又輸出一個大寫字母A,接著輸出一個轉義字符“\n”,相當于輸出一個換行符。第6行先輸出一個數值常量123,接著輸出一個轉義字符“\x20”,相當于輸出一個空栺,接著輸出轉義字符“\'”,相當于輸出一個單引號,接下來又輸出空栺、雙引號,最后輸出換行符。
4.5.2 字符變量

字符變量中所存放的字符是計算機字符集中的字符。對于PC上運行的C系統,字符型數據用8位單字節的ASCII碼表示。
程序用類型說明符char聲明字符變量。例如:
char ch; /*聲明一個字符變量*/
這條聲明語句聲明了一個字符變量ch。當以這種形式聲明變量之后,程序可以在表達式中引用這個變量,關于語句和表達式的知識在后面將會介紹。
例如,將字符'a'賦值給上面定義的字符變量ch:
ch='a';
因為'a'的ASCII值是97,所以字符變量ch在內存中的表示如下。

在C語言中,可以將字符變量看成是整型變量。允許對整型變量賦以字符值,也允許對字符變量賦以整型值。在輸出時,允許把字符變量按整型量輸出,也允許把整型變量按字符變量輸出。
整型變量為雙字節變量,字符變量為單字節變量,當整型變量按字符型變量處理時,只有低8位字節參與處理。
字符型數據分為一般字符型(char)、有符號字符型(signed char)和無符號字符型(unsigned char)3種。
可以將存儲在字符變量中的整數解釋成一個有符號的值,也可以解釋成無符號的值,如何解釋完全取決于編譯器。有符號字符型允許存儲正數,也允許存儲負數,其取值范圍是-128~127。無符號字符型只允許存放正數,其取值范圍為0~255,如表4-5所示。
表4-5 字符變量的取值范圍

【例4-10】將字符變量賦予字符值。
(1)在Visual C++6.0中,新建名稱為4-10.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-11所示。

圖4-11 程序運行結果10
此例中a、b被聲明為字符變量并賦以字符值,C語言允許字符變量參與數值運算,即用字符的ASCII值參與運算。由于大小寫字母的ASCII值相差32,所以運算后把小寫字母換成大寫字母,然后分別以整型和字符型輸出。
【例4-11】向字符變量賦以整數。
(1)在Visual C++6.0中,新建名稱為4-11.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。
main() { char a,b; a=120; b=121; printf("%c,%c\n",a,b); printf("%d,%d\n",a,b); }
(3)程序運行結果如圖4-12所示。

圖4-12 程序運行結果11
本例中聲明a、b為字符變量,但在賦值語句中賦以整型值。從結果看,a、b值的輸出形式取決于printf()函數栺式串中的栺式符。當栺式符為“%c”時,對應輸出的變量值為字符,當栺式符為“%d”時,對應輸出的變量值為整數。
4.5.3 字符串常量

字符串常量是由一對雙引號括起的字符序列。例如,"CHINA"、"C program"、"$12.5"等都是合法的字符串常量。
字符串常量和字符常量是不同的量,它們之間主要有以下區別。
(1)字符常量由單引號引起來,字符串常量由雙引號引起來。
(2)字符常量只能是單個字符,字符串常量則可以含一個或多個字符。
(3)可以把一個字符常量賦予一個字符變量,但不能把一個字符串常量賦給一個字符變量。在C語言中沒有相應的字符串變量,這是與BASIC語言不同的。但是可以用一個字符數組來存放一個字符串常量。
(4)字符常量占一個字節的內存空間。字符串常量占的內存字節數等于字符串占的字節數加1。增加的一個字節中存放字符"\0"(ASCII值為0),這是字符串結束的標志。
例如,字符串"C program"在內存中所占的字節為:

字符常量'a'和字符串常量"a"雖然都只有一個字符,但在內存中的情況是不同的。
'a'在內存中占一個字節,可表示為:

"a"在內存中占兩個字節,可表示為:

4.6 數據類型的轉換
在計算過程中,如果遇到不同的數據類型參與運算該怎么辦?是終止程序,還是轉換類型后繼續計算?編譯器采取第二種方式,能夠轉換成功的繼續運算,轉換失敗時程序再報錯終止運行。數據類型的轉換有兩種方式:隱式轉換和顯式轉換。
4.6.1 隱式轉換

C語言中設定了不同數據參與運算時的轉換觃則,編譯器就會悄無聲息地進行數據類型的轉換,進而計算出最終結果,這就是隱式轉換。隱式轉換又稱為自動轉換,轉換時應遵循以下觃則。
(1)若參與運算的量類型不同,則先轉換成同一類型,然后再進行運算。
(2)轉換按數據長度增加的方向進行,以保證精度不降低。如在int型和long型運算時,先將int型轉換成long型后再進行運算。
(3)所有的浮點運算都是以雙精度進行的,即使僅含float型量運算的表達式,也要先轉換成double型,再做運算。
(4)在char型和short型參與運算時,必須先將它們轉換成int型。
(5)在賦值運算中,賦值號兩邊量的數據類型不同時,賦值號右邊量的類型將轉換成左邊量的類型。如果右邊量的數據類型長度比左邊長時,將丟失一部分數據,這樣會降低精度,丟失的部分按四舍五入向前舍入。
隱式轉換如圖4-13所示。

圖4-13 隱式轉換
例如:

先計算“=”號右邊的表達式,字符型和整型混合運算按照數據類型轉換先后順序,把字符型'A'轉換為整型,其值為65,然后求和得67,最后把67賦給變量i。
又如:

先計算“=”號右邊的表達式,字符型、整型和浮點型混合運算,因為有浮點型參與運算,所以“=”右邊表達式的結果是浮點型。按照數據類型轉換順序,把字符型'A'轉換為雙精度型為65.0,2轉換為2.0,1.5F轉換為1.5,最后把雙精度浮點數68.5賦給變量d。
上述情況都是由低精度類型向高精度類型轉換。如果逆向轉換,可能會出現丟失數據的危險,編譯器會以警告的形式給出提示。例如:

浮點數1.2舍棄小數位后,把整數部分1賦給變量i。如果i=1.9,運算后變量i的值依然是1,而不是2。
【例4-12】隱式轉換。
(1)在Visual C++6.0中,新建名稱為4-12.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-14所示。

圖4-14 程序運行結果12
本例中,PI為浮點型;s,r為整型。在執行s=r*r*PI語句時,r和PI都轉換成雙精度型計算,結果也為雙精度型。但由于s為整型,故賦值結果仌為整型,舍去了小數部分。
4.6.2 顯式轉換

進行隱式轉換編譯器會產生警告,提示程序存在潛在的隱患。如果非常明確地希望轉換數據類型,就需要用顯式轉換。
其一般形式為:
(類型說明符) (表達式)
其功能是把表達式的運算結果強制轉換成類型說明符所表示的類型。
例如:

在使用顯式轉換時應注意以下問題。
(1)類型說明符和表達式都必須加括號(單個變量可以不加括號),如把(int)(x+y)寫成(int)x+y則表示把x轉換成int型之后再與y相加。
(2)無論是強制轉換還是自動轉換,都只是為了本次運算的需要而對變量的類型進行的臨時性轉換,不改變數據說明時對該變量定義的類型。
【例4-13】強制類型轉換。
(1)在Visual C++6.0中,新建名稱為4-13.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-15所示。

圖4-15 程序運行結果13
本例表明,f雖強制轉換為int型,但只在運算中起作用,是臨時的,而f本身的類型并不改變。因此,(int)f的值為5(初去了小數),而f的值仌為5.75。
4.7 使用typedef定義類型
typedef聲明簡稱typedef,是為現有類型創建一個易于記憶的新名字和簡化一些比較復雜類型的聲明。使用typedef可增加代碼的美觀性和可讀性,美觀指typedef能隱藏笨拙的語法結構以及與平臺相關的數據類型,從而增強程序的可移植性和可維護性。
4.7.1 促進跨平臺開發

用typedef可定義與平臺無關的類型。
定義一個稱為BAN的浮點類型,在平臺一上,讓它表示的最高精度類型為:
typedef long double BAN;
在不支持long double的平臺二上,改為:
typedef double BAN;
在連double都不支持的平臺三上,改為:
typedef float BAN;
當跨平臺時,只用修改typedef本身,不用對其他源代碼做仸何修改。
4.7.2 定義類型別名

typedef使用最多的地方是創建易于記憶的類型名,用它來將程序員的意圖歸檔。但是typedef并不創建新的類型,它僅僅為現有類型添加一個同義字。
1. 定義基本數據類型別名
基本數據類型出現在所聲明的變量名字中,位于typedef關鍵字右邊。例如:
typedef int size; size arr;
此聲明定義了一個int的同義字,名字為size。注意,typedef并不是創建新的類型,而是為現有類型添加一個同義詞。
2. 定義數組別名
例如:
typedef int array [2];
此聲明定義了一個int型數組的同義字,名字為array。因此,array等價于int [2]定義;array a聲明等價于int a[2]的聲明。
3. 定義指針別名
例如:
char* pa, pb;
此聲明表示只定義了一個指向字符變量的指針pa和一個字符變量pb。
若使用typedef定義指針別名,則可以用作同時聲明指針型的多個對象。例如:

此聲明定義了兩個指向字符變量的指針pa和pb。
4. 定義結構體別名
例如:

此聲明定義了一個結構體的同義字,名字為Stu。
4.7.3 定義復雜的聲明別名

為復雜的聲明定義一個新的簡單的別名。只要在原來的聲明里逐步用別名替換一部分復雜聲明,如此循環,把帶變量名的部分留到最后替換,得到的就是原聲明的最簡化版。例如:
(1)原聲明為“int *(*a[5])(int, char*);”。使用typedef定義一個新別名pFun,可以直接替換a。例如:
typedef int *(*pFun)(int, char*); pFun a[5];
(2)原聲明為“void (*b[10]) (void (*)());”。此聲明中變量名為b,先替換右邊部分括號里的變量,pFunParam為別名一:
typedef void (*pFunParam)();
再替換左邊的變量b,pFunx為別名二:
typedef void (*pFunx)(pFunParam);
原聲明的最簡化版:
pFunx b[10];
(3)原聲明為“doube(*)() (*e)[9];”。此聲明中變量名為e,先替換左邊部分,pFuny為別名一:
typedef double(*pFuny)();
再替換右邊的變量e,pFunParamy為別名二:
typedef pFuny (*pFunParamy)[9];
原聲明的最簡化版:
pFunParamy e;
理解復雜聲明可用“右左法則”:從變量名看起,先彽右,再彽左,碰到一個圓括號就調轉閱讀的方向;括號內分析完就跳出括號,還是按先右后左的順序,如此循環,直到整個聲明分析完。
4.7.4 typedef與#define

#define是預處理指令,在編譯預處理時進行簡單的替換,不進行正確性檢查。而typedef是在編譯時處理的,它在自己的作用域內給一個已經存在的類型定義一個別名,使用typedef定義的變量類型的作用范圍為所定義的函數或者文件內。例如:
typedef int* ptr; #define ptr int*
二者都是用ptr代表int*,但是二者又不同。后者定義后,“ptr a,b;”相當于“int* a,b;”,只是簡單的宏替換;而前者定義后,typedef為int*引入了一個新的助記符,“ptr a,b”中的a,b都為指向int的指針。
注意,typedef是定義了一種類型的新別名,不同于宏,它不是簡單的字符串替換。
typedef char* ptr; int strcmp(const ptr,const ptr);
const ptr不等同于const char*,它實際上相當于char* const。原因在于const給予了整個指針本身以常量性。簡單來說,當const和typedef一起出現時,typedef不是簡單的字符串替換。

只要為指針聲明typedef,都要在最終的typedef名稱中加一個const,以使該指針本身是常量,而不是對象。
注意:typedef在語法上是一個存儲類的關鍵字,例如auto、extern、mutable、static、register等,雖然它并不真正影響對象的存儲特性。
4.8 綜合案例——類型轉換

【例4-14】綜合應用數據類型和類型轉換實例。
(1)在Visual C++6.0中,新建名稱為4-14.c的Text File文件。
(2)在代碼編輯區域輸入以下代碼。

(3)程序運行結果如圖4-16所示。

圖4-16 程序運行結果14
在例4-14中綜合了本章的知識點,三種進制間的數據類型轉換輸出字符'a',轉義符分別使用輸出字符'a'的單引號和換行,注意隱式數據類型轉換是否丟失了數據,以及顯式數據類型轉換的對象。
4.9 就業面試技巧與解析
數據類型是所有程序語言的基礎。C程序的所有功能都是建立在基本數據類型之上。數據類型可以告訴編譯器每個數據所代表的含義,以及程序對數據的操作。它確定了數據和數據操作在程序中的意義。本章節涉及的細節問題較多,可以考查面試者的基本功,因此是面試考官經常會問到的部分。
4.9.1 面試技巧與解析(一)
面試官:字符型常量和字符串常量有什么區別?
應聘者:字符型常量與字符串常量的書寫方式不同,用單引號引起來的字符是字符常量,用雙引號引起來的字符是字符串常量。字符串常量與字符型常量的存儲方式不同,C編譯程序在存儲字符串常量時,自動采用\0作為字符串常量結束的標志。
4.9.2 面試技巧與解析(二)
面試官:字符變量在內存中如何存儲?
應聘者:字符是字符集中的一系列符號,在使用時用編號進行讀取,也就是說在字符變量里面放著這些符號的編號,這些編號就是ASCII碼。ASCII碼與一些整型數據對應,這也造成了字符型變量在一定程度上可以和整型變量進行換算。因為數字1~9的ASCII碼是連續的,而且是整型,所以可以在輸入數字字符時,在程序內部將其換算成真正的整型數據來處理(必要的時候)。字母的ASCII碼從A~Z和a~z都分別是連續的,這也使得我們可以方便地進行大小寫的轉換。字符型變量可以讓我們更方便地處理一些輸入問題。