国产精品天干天干,亚洲毛片在线,日韩gay小鲜肉啪啪18禁,女同Gay自慰喷水

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

【轉(zhuǎn)載】C Primer Plus(第6版):第3章 數(shù)據(jù)和C

2023-12-07 01:38 作者:安塞腰果樂  | 我要投稿

第3章 數(shù)據(jù)和C

本章介紹以下內(nèi)容:

?關(guān)鍵字:int 、short、long、unsigned、char、float、double、_Bool、_Complex、_Imaginary

?運(yùn)算符:sizeof()

?函數(shù):scanf()

?整數(shù)類型和浮點(diǎn)數(shù)類型的區(qū)別

?如何書寫整型和浮點(diǎn)型常數(shù),如何聲明這些類型的變量

?如何使用printf()和scanf()函數(shù)讀寫不同類型的值

程序離不開數(shù)據(jù)。把數(shù)字、字母和文字輸入計(jì)算機(jī),就是希望它利用這些數(shù)據(jù)完成某些任務(wù)。例如,需要計(jì)算一份利息或顯示一份葡萄酒商的排序列表。本章除了介紹如何讀取數(shù)據(jù)外,還將教會讀者如何操控?cái)?shù)據(jù)。

C 語言提供兩大系列的多種數(shù)據(jù)類型。本章詳細(xì)介紹兩大數(shù)據(jù)類型:整數(shù)類型和浮點(diǎn)數(shù)類型,講解這些數(shù)據(jù)類型是什么、如何聲明它們、如何以及何時(shí)使用它們。除此之外,還將介紹常量和變量的區(qū)別。讀者很快就能看到第1個交互式程序。

3.1 示例程序

本章仍從一個簡單的程序開始。如果發(fā)現(xiàn)有不熟悉的內(nèi)容,別擔(dān)心,我們稍后會詳細(xì)解釋。該程序的意圖比較明了,請?jiān)囍幾g并運(yùn)行程序清單3.1中的源代碼。為了節(jié)省時(shí)間,在輸入源代碼時(shí)可省略注釋。

程序清單3.1 platinum.c程序

/* platinum.c -- your weight in platinum */

#include <stdio.h>

int main(void)

{

? ? float weight; /* 你的體重 */

? ? float value; /* 相等重量的白金價(jià)值 */

? ? printf("Are you worth your weight in platinum?\n");

? ? printf("Let's check it out.\n");

? ? printf("Please enter your weight in pounds: ");

? ? /* 獲取用戶的輸入 */

? ? scanf("%f", &weight);

? ? /* 假設(shè)白金的價(jià)格是每盎司$1700 */

? ? /* 14.5833用于把英鎊常衡盎司轉(zhuǎn)換為金衡盎司[1]*/

? ? value = 1700.0 * weight * 14.5833;

? ? printf("Your weight in platinum is worth $%.2f.\n", value);

? ? printf("You are easily worth that! If platinum prices drop,\n");

? ? printf("eat more to maintain your value.\n");

? ? return 0;

}

提示 錯誤與警告

如果輸入程序時(shí)打錯(如,漏了一個分號),編譯器會報(bào)告語法錯誤消息。然而,即使輸入正確無誤,編譯器也可能給出一些警告,如“警告:從double類型轉(zhuǎn)換成float類型可能會丟失數(shù)據(jù)”。錯誤消息表明程序中有錯,不能進(jìn)行編譯。而警告則表明,盡管編寫的代碼有效,但可能不是程序員想要的。警告并不終止編譯。特殊的警告與C如何處理1700.0這樣的值有關(guān)。本例不必理會這個問題,本章稍后會進(jìn)一步說明。

輸入該程序時(shí),可以把1700.0改成貴金屬白金當(dāng)前的市價(jià),但是不要改動14.5833,該數(shù)是1英鎊的金衡盎司數(shù)(金衡盎司用于衡量貴金屬,而英鎊常衡盎司用于衡量人的體重)。

注意,“enter your weight”的意思是輸入你的體重,然后按下Enter或Return鍵(不要鍵入體重后就一直等著)。按下Enter鍵是告知計(jì)算機(jī),你已完成輸入數(shù)據(jù)。該程序需要你輸入一個數(shù)字(如,155),而不是單詞(如,too much)。如果輸入字母而不是數(shù)字,會導(dǎo)致程序出問題。這個問題要用if語句來解決(詳見第7章),因此請先輸入數(shù)字。下面是程序的輸出示例:

Are you worth your weight in platinum?

Let's check it out.

Please enter your weight in pounds: 156

Your weight in platinum is worth $3867491.25.

You are easily worth that! If platinum prices drop,

eat more to maintain your value.

程序調(diào)整

即使用第2章介紹的方法,在程序中添加下面一行代碼:

getchar();

程序的輸出是否依舊在屏幕上一閃而過?本例,需要調(diào)用兩次getchar()函數(shù):

getchar();

getchar();

getchar()函數(shù)讀取下一個輸入字符,因此程序會等待用戶輸入。在這種情況下,鍵入 156 并按下Enter(或Return)鍵(發(fā)送一個換行符),然后scanf()讀取鍵入的數(shù)字,第1個getchar()讀取換行符,第2個getchar()讓程序暫停,等待輸入。

3.1.1 程序中的新元素

程序清單3.1中包含C語言的一些新元素。注意,代碼中使用了一種新的變量聲明。前面的例子中只使用了整數(shù)類型的變量(int),但是本例使用了浮點(diǎn)數(shù)類型(float)的變量,以便處理更大范圍的數(shù)據(jù)。float 類型可以儲存帶小數(shù)的數(shù)字。

程序中演示了常量的幾種新寫法?,F(xiàn)在可以使用帶小數(shù)點(diǎn)的數(shù)了。

為了打印新類型的變量,在printf()中使用%f來處理浮點(diǎn)值。%.2f中的.2用于精確控制輸出,指定輸出的浮點(diǎn)數(shù)只顯示小數(shù)點(diǎn)后面兩位。?

scanf()函數(shù)用于讀取鍵盤的輸入。%f說明scanf()要讀取用戶從鍵盤輸入的浮點(diǎn)數(shù),&weight告訴 scanf()把輸入的值賦給名為 weight 的變量。scanf()函數(shù)使用&符號表明找到 weight變量的地點(diǎn)。下一章將詳細(xì)討論&。就目前而言,請按照這樣寫。

也許本程序最突出的新特點(diǎn)是它的交互性。計(jì)算機(jī)向用戶詢問信息,然后用戶輸入數(shù)字。與非交互式程序相比,交互式程序用起來更有趣。更重要的是,交互式使得程序更加靈活。例如,示例程序可以使用任何合理的體重,而不只是 156磅。不必重寫程序,就可以根據(jù)不同體重進(jìn)行計(jì)算。scanf()和printf()函數(shù)用于實(shí)現(xiàn)這種交互。scanf()函數(shù)讀取用戶從鍵盤輸入的數(shù)據(jù),并把數(shù)據(jù)傳遞給程序;printf()函數(shù)讀取程序中的數(shù)據(jù),并把數(shù)據(jù)顯示在屏幕上。把兩個函數(shù)結(jié)合起來,就可以建立人機(jī)雙向通信(見圖 3.1),這讓使用計(jì)算機(jī)更加饒有趣味。

本章著重解釋上述新特性中的前兩項(xiàng):各種數(shù)據(jù)類型的變量和常量。第4章將介紹后3項(xiàng)。?

3.2 變量與常量數(shù)據(jù)

在程序的指導(dǎo)下,計(jì)算機(jī)可以做許多事情,如數(shù)值計(jì)算、名字排序、執(zhí)行語言或視頻命令、計(jì)算彗星軌道、準(zhǔn)備郵件列表、撥電話號碼、畫畫、做決策或其他你能想到的事情。要完成這些任務(wù),程序需要使用數(shù)據(jù),即承載信息的數(shù)字和字符。有些數(shù)據(jù)類型在程序使用之前已經(jīng)預(yù)先設(shè)定好了,在整個程序的運(yùn)行過程中沒有變化,這些稱為常量(constant)。其他數(shù)據(jù)類型在程序運(yùn)行期間可能會改變或被賦值,這些稱為變量(variable)。在示例程序中,weight 是一個變量,14.5833 是一個常量。那么,1700.0 是常量還是變量?在現(xiàn)實(shí)生活中,白金的價(jià)格不會是常量,但是在程序中,像1700.0這樣的價(jià)格被視為常量。

3.3 數(shù)據(jù):數(shù)據(jù)類型關(guān)鍵字

不僅變量和常量不同,不同的數(shù)據(jù)類型之間也有差異。一些數(shù)據(jù)類型表示數(shù)字,一些數(shù)據(jù)類型表示字母(更普遍地說是字符)。C通過識別一些基本的數(shù)據(jù)類型來區(qū)分和使用這些不同的數(shù)據(jù)類型。如果數(shù)據(jù)是常量,編譯器一般通過用戶書寫的形式來識別類型(如,42是整數(shù),42.100是浮點(diǎn)數(shù))。但是,對變量而言,要在聲明時(shí)指定其類型。稍后會詳細(xì)介紹如何聲明變量?,F(xiàn)在,我們先來了解一下C語言的基本類型關(guān)鍵字。K&C給出了7個與類型相關(guān)的關(guān)鍵字。C90標(biāo)準(zhǔn)添加了2個關(guān)鍵字,C99標(biāo)準(zhǔn)又添加了3個關(guān)鍵字(見表3.1)。

在C語言中,用int關(guān)鍵字來表示基本的整數(shù)類型。后3個關(guān)鍵字(long、short和unsigned)和C90新增的signed用于提供基本整數(shù)類型的變式,例如unsigned short int和long long int。char關(guān)鍵字用于指定字母和其他字符(如,#、$、%和*)。另外,char類型也可以表示較小的整數(shù)。float、double和long double表示帶小數(shù)點(diǎn)的數(shù)。_Bool類型表示布爾值(true或false),_complex和_Imaginary分別表示復(fù)數(shù)和虛數(shù)。

通過這些關(guān)鍵字創(chuàng)建的類型,按計(jì)算機(jī)的儲存方式可分為兩大基本類型:整數(shù)類型和浮點(diǎn)數(shù)類型。

位、字節(jié)和字

位、字節(jié)和字是描述計(jì)算機(jī)數(shù)據(jù)單元或存儲單元的術(shù)語。這里主要指存儲單元。

最小的存儲單元是位(bit),可以儲存0或1(或者說,位用于設(shè)置“開”或“關(guān)”)。雖然1位儲存的信息有限,但是計(jì)算機(jī)中位的數(shù)量十分龐大。位是計(jì)算機(jī)內(nèi)存的基本構(gòu)建塊。

字節(jié)(byte)是常用的計(jì)算機(jī)存儲單位。對于幾乎所有的機(jī)器,1字節(jié)均為8位。這是字節(jié)的標(biāo)準(zhǔn)定義,至少在衡量存儲單位時(shí)是這樣(但是,C語言對此有不同的定義,請參閱本章3.4.3節(jié))。既然1位可以表示0或1,那么8位字節(jié)就有256(2的8次方)種可能的0、1的組合。通過二進(jìn)制編碼(僅用0和1便可表示數(shù)字),便可表示0~255的整數(shù)或一組字符(第15章將詳細(xì)討論二進(jìn)制編碼,如果感興趣可以現(xiàn)在瀏覽一下該章的內(nèi)容)。

字(word)是設(shè)計(jì)計(jì)算機(jī)時(shí)給定的自然存儲單位。對于8位的微型計(jì)算機(jī)(如,最初的蘋果機(jī)), 1個字長只有8位。從那以后,個人計(jì)算機(jī)字長增至16位、32位,直到目前的64位。計(jì)算機(jī)的字長越大,其數(shù)據(jù)轉(zhuǎn)移越快,允許的內(nèi)存訪問也更多。

3.3.1 整數(shù)和浮點(diǎn)數(shù)

整數(shù)類型?浮點(diǎn)數(shù)類型?如果覺得這些術(shù)語非常陌生,別擔(dān)心,下面先簡述它們的含義。如果不熟悉位、字節(jié)和字的概念,請閱讀上面方框中的內(nèi)容。剛開始學(xué)習(xí)時(shí),不必了解所有的細(xì)節(jié),就像學(xué)習(xí)開車之前不必詳細(xì)了解汽車內(nèi)部引擎的原理一樣。但是,了解一些計(jì)算機(jī)或汽車引擎內(nèi)部的原理會對你有所幫助。

對我們而言,整數(shù)和浮點(diǎn)數(shù)的區(qū)別是它們的書寫方式不同。對計(jì)算機(jī)而言,它們的區(qū)別是儲存方式不同。下面詳細(xì)介紹整數(shù)和浮點(diǎn)數(shù)。

3.3.2 整數(shù)

和數(shù)學(xué)的概念一樣,在C語言中,整數(shù)是沒有小數(shù)部分的數(shù)。例如,2、?23和2456都是整數(shù)。而3.14、0.22和2.000都不是整數(shù)。計(jì)算機(jī)以二進(jìn)制數(shù)字儲存整數(shù),例如,整數(shù)7以二進(jìn)制寫是111。因此,要在8位字節(jié)中儲存該數(shù)字,需要把前5位都設(shè)置成0,后3位設(shè)置成1(如圖3.2所示)。

3.3.3 浮點(diǎn)數(shù)

浮點(diǎn)數(shù)與數(shù)學(xué)中實(shí)數(shù)的概念差不多。2.75、3.16E7、7.00 和 2e-8 都是浮點(diǎn)數(shù)。注意,在一個值后面加上一個小數(shù)點(diǎn),該值就成為一個浮點(diǎn)值。所以,7是整數(shù),7.00是浮點(diǎn)數(shù)。顯然,書寫浮點(diǎn)數(shù)有多種形式。稍后將詳細(xì)介紹e記數(shù)法,這里先做簡要介紹:3.16E7 表示3.16×10^7(3.16 乘以10 的7次方)。其中, 10^7=10000000,7被稱為10的指數(shù)。

這里關(guān)鍵要理解浮點(diǎn)數(shù)和整數(shù)的儲存方案不同。計(jì)算機(jī)把浮點(diǎn)數(shù)分成小數(shù)部分和指數(shù)部分來表示,而且分開儲存這兩部分。因此,雖然7.00和7在數(shù)值上相同,但是它們的儲存方式不同。在十進(jìn)制下,可以把7.0寫成0.7E1。這里,0.7是小數(shù)部分,1是指數(shù)部分。圖3.3演示了一個儲存浮點(diǎn)數(shù)的例子。當(dāng)然,計(jì)算機(jī)在內(nèi)部使用二進(jìn)制和2的冪進(jìn)行儲存,而不是10的冪。第15章將詳述相關(guān)內(nèi)容。現(xiàn)在,我們著重講解這兩種類型的實(shí)際區(qū)別。

?整數(shù)沒有小數(shù)部分,浮點(diǎn)數(shù)有小數(shù)部分。

?浮點(diǎn)數(shù)可以表示的范圍比整數(shù)大。參見本章末的表3.3。

?對于一些算術(shù)運(yùn)算(如,兩個很大的數(shù)相減),浮點(diǎn)數(shù)損失的精度更多。

?因?yàn)樵谌魏螀^(qū)間內(nèi)(如,1.0 到 2.0 之間)都存在無窮多個實(shí)數(shù),所以計(jì)算機(jī)的浮點(diǎn)數(shù)不能表示區(qū)間內(nèi)所有的值。浮點(diǎn)數(shù)通常只是實(shí)際值的近似值。例如,7.0可能被儲存為浮點(diǎn)值6.99999。稍后會討論更多精度方面的內(nèi)容。

?過去,浮點(diǎn)運(yùn)算比整數(shù)運(yùn)算慢。不過,現(xiàn)在許多CPU都包含浮點(diǎn)處理器,縮小了速度上的差距。

3.4 C語言基本數(shù)據(jù)類型

本節(jié)將詳細(xì)節(jié)介紹C語言的基本數(shù)據(jù)類型,包括如何聲明變量、如何表示字面值常量(如,5或2.78),以及典型的用法。一些老式的C語言編譯器無法支持這里提到的所有類型,請查閱你使用的編譯器文檔,了解可以使用哪些類型。

3.4.1 int類型

C語言提供了許多整數(shù)類型,為什么一種類型不夠用?因?yàn)?C語言讓程序員針對不同情況選擇不同的類型。特別是,C語言中的整數(shù)類型可表示不同的取值范圍和正負(fù)值。一般情況使用int類型即可,但是為滿足特定任務(wù)和機(jī)器的要求,還可以選擇其他類型。

int類型是有符號整型,即int類型的值必須是整數(shù),可以是正整數(shù)、負(fù)整數(shù)或零。其取值范圍依計(jì)算機(jī)系統(tǒng)而異。一般而言,儲存一個int要占用一個機(jī)器字長。因此,早期的16位IBM PC兼容機(jī)使用16位來儲存一個int值,其取值范圍(即int值的取值范圍)是-32768~32767。目前的個人計(jì)算機(jī)一般是32位,因此用32位儲存一個int值。現(xiàn)在,個人計(jì)算機(jī)產(chǎn)業(yè)正逐步向著64位處理器發(fā)展,自然能儲存更大的整數(shù)。ISO C規(guī)定int的取值范圍最小為-32768~32767。一般而言,系統(tǒng)用一個特殊位的值表示有符號整數(shù)的正負(fù)號。第15章將介紹常用的方法。

1.聲明int變量

第2章中已經(jīng)用int聲明過基本整型變量。先寫上int,然后寫變量名,最后加上一個分號。要聲明多個變量,可以單獨(dú)聲明每個變量,也可在int后面列出多個變量名,變量名之間用逗號分隔。下面都是有效的聲明:

int erns;

int hogs, cows, goats;

?

可以分別在4條聲明中聲明各變量,也可以在一條聲明中聲明4個變量。兩種方法的效果相同,都為4個int大小的變量賦予名稱并分配內(nèi)存空間。

以上聲明創(chuàng)建了變量,但是并沒有給它們提供值。變量如何獲得值?前面介紹過在程序中獲取值的兩種途徑。第1種途徑是賦值:

cows = 112;

第2種途徑是,通過函數(shù)(如,scanf())獲得值。接下來,我們著重介紹第3種途徑。

2.初始化變量

初始化(initialize)變量就是為變量賦一個初始值。在C語言中,初始化可以直接在聲明中完成。只需在變量名后面加上賦值運(yùn)算符(=)和待賦給變量的值即可。如下所示:

int hogs = 21;

int cows = 32, goats = 14;

int dogs, cats = 94; /* 有效,但是這種格式很糟糕 */

以上示例的最后一行,只初始化了cats,并未初始化dogs。這種寫法很容易讓人誤認(rèn)為dogs也被初始化為94,所以最好不要把初始化的變量和未初始化的變量放在同一條聲明中。簡而言之,聲明為變量創(chuàng)建和標(biāo)記存儲空間,并為其指定初始值(如圖3.4所示)。

?

3.int類型常量

上面示例中出現(xiàn)的整數(shù)(21、32、14和94)都是整型常量或整型字面量。C語言把不含小數(shù)點(diǎn)和指數(shù)的數(shù)作為整數(shù)。因此,22和-44都是整型常量,但是22.0和2.2E1則不是。C語言把大多數(shù)整型常量視為int類型,但是非常大的整數(shù)除外。詳見后面“l(fā)ong常量和long long常量”小節(jié)對long int類型的討論。

4.打印int值

可以使用printf()函數(shù)打印int類型的值。第2章中介紹過,%d指明了在一行中打印整數(shù)的位置。%d稱為轉(zhuǎn)換說明,它指定了printf()應(yīng)使用什么格式來顯示一個值。格式化字符串中的每個%d都與待打印變量列表中相應(yīng)的int值匹配。這個值可以是int類型的變量、int類型的常量或其他任何值為int類型的表達(dá)式。作為程序員,要確保轉(zhuǎn)換說明的數(shù)量與待打印值的數(shù)量相同,編譯器不會捕獲這類型的錯誤。程序清單3.2演示了一個簡單的程序,程序中初始化了一個變量,并打印該變量的值、一個常量值和一個簡單表達(dá)式的值。另外,程序還演示了如果粗心犯錯會導(dǎo)致什么結(jié)果。

程序清單3.2 print1.c程序

/* print1.c - 演示printf()的一些特性 */

#include <stdio.h>

int main(void)

{

? ? int ten = 10;

? ? int two = 2;

? ? printf("Doing it right: ");

? ? printf("%d minus %d is %d\n", ten, 2, ten - two);

? ? printf("Doing it wrong: ");

? ? printf("%d minus %d is %d\n", ten); // 遺漏2個參數(shù)

? ? return 0;

}

編譯并運(yùn)行該程序,輸出如下:

Doing it right: 10 minus 2 is 8

Doing it wrong: 10 minus 16 is 1650287143

在第一行輸出中,第1個%d對應(yīng)int類型變量ten;第2個%d對應(yīng)int類型常量2;第3個%d對應(yīng)int類型表達(dá)式ten - two的值。在第二行輸出中,第1個%d對應(yīng)ten的值,但是由于沒有給后兩個%d提供任何值,所以打印出的值是內(nèi)存中的任意值(讀者在運(yùn)行該程序時(shí)顯示的這兩個數(shù)值會與輸出示例中的數(shù)值不同,因?yàn)閮?nèi)存中儲存的數(shù)據(jù)不同,而且編譯器管理內(nèi)存的位置也不同)。

你可能會抱怨編譯器為何不能捕獲這種明顯的錯誤,但實(shí)際上問題出在printf()不尋常的設(shè)計(jì)。大部分函數(shù)都需要指定數(shù)目的參數(shù),編譯器會檢查參數(shù)的數(shù)目是否正確。但是,printf()函數(shù)的參數(shù)數(shù)目不定,可以有1個、2個、3個或更多,編譯器也愛莫能助。記住,使用printf()函數(shù)時(shí),要確保轉(zhuǎn)換說明的數(shù)量與待打印值的數(shù)量相等。

5.八進(jìn)制和十六進(jìn)制

通常,C語言都假定整型常量是十進(jìn)制數(shù)。然而,許多程序員很喜歡使用八進(jìn)制和十六進(jìn)制數(shù)。因?yàn)?和16都是2的冪,而10卻不是。顯然,八進(jìn)制和十六進(jìn)制記數(shù)系統(tǒng)在表達(dá)與計(jì)算機(jī)相關(guān)的值時(shí)很方便。例如,十進(jìn)制數(shù)65536經(jīng)常出現(xiàn)在16位機(jī)中,用十六進(jìn)制表示正好是10000。另外,十六進(jìn)制數(shù)的每一位的數(shù)恰好由4位二進(jìn)制數(shù)表示。例如,十六進(jìn)制數(shù)3是0011,十六進(jìn)制數(shù)5是0101。因此,十六進(jìn)制數(shù)35的位組合(bit pattern)是00110101,十六進(jìn)制數(shù)53的位組合是01010011。這種對應(yīng)關(guān)系使得十六進(jìn)制和二進(jìn)制的轉(zhuǎn)換非常方便。但是,計(jì)算機(jī)如何知道10000是十進(jìn)制、十六進(jìn)制還是二進(jìn)制?在C語言中,用特定的前綴表示使用哪種進(jìn)制。0x或0X前綴表示十六進(jìn)制值,所以十進(jìn)制數(shù)16表示成十六進(jìn)制是0x10或0X10。與此類似,0前綴表示八進(jìn)制。例如,十進(jìn)制數(shù)16表示成八進(jìn)制是020。第15章將更全面地介紹進(jìn)制相關(guān)的內(nèi)容。

要清楚,使用不同的進(jìn)制數(shù)是為了方便,不會影響數(shù)被儲存的方式。也就是說,無論把數(shù)字寫成16、020或0x10,儲存該數(shù)的方式都相同,因?yàn)橛?jì)算機(jī)內(nèi)部都以二進(jìn)制進(jìn)行編碼。

6.顯示八進(jìn)制和十六進(jìn)制

在C程序中,既可以使用和顯示不同進(jìn)制的數(shù)。不同的進(jìn)制要使用不同的轉(zhuǎn)換說明。以十進(jìn)制顯示數(shù)字,使用%d;以八進(jìn)制顯示數(shù)字,使用%o;以十六進(jìn)制顯示數(shù)字,使用%x。另外,要顯示各進(jìn)制數(shù)的前綴0、0x和0X,必須分別使用%#o、%#x、%#X。程序清單3.3演示了一個小程序?;貞浺幌?,在某些集成開發(fā)環(huán)境(IDE)下編寫的代碼中插入getchar();語句,程序

在執(zhí)行完畢后不會立即關(guān)閉執(zhí)行窗口。

程序清單3.3 bases.c程序

/* bases.c--以十進(jìn)制、八進(jìn)制、十六進(jìn)制打印十進(jìn)制數(shù)100 */

#include <stdio.h>

int main(void)

{

? ? int x = 100;

? ? printf("dec = %d; octal = %o; hex = %x\n", x, x, x);

? ? printf("dec = %d; octal = %#o; hex = %#x\n", x, x, x);

? ? return 0;

}

編譯并運(yùn)行該程序,輸出如下:

dec = 100; octal = 144; hex = 64

dec = 100; octal = 0144; hex = 0x64

該程序以3種不同記數(shù)系統(tǒng)顯示同一個值。printf()函數(shù)做了相應(yīng)的轉(zhuǎn)換。注意,如果要在八進(jìn)制和十六進(jìn)制值前顯示0和0x前綴,要分別在轉(zhuǎn)換說明中加入#。

3.4.2 其他整數(shù)類型

初學(xué)C語言時(shí),int類型應(yīng)該能滿足大多數(shù)程序的整數(shù)類型需求。盡管如此,還應(yīng)了解一下整型的其他形式。當(dāng)然,也可以略過本節(jié)跳至3.4.3節(jié)閱讀char類型的相關(guān)內(nèi)容,以后有需要時(shí)再閱讀本節(jié)。

C語言提供3個附屬關(guān)鍵字修飾基本整數(shù)類型:short、long和unsigned。應(yīng)記住以下幾點(diǎn)。

?short int類型(或者簡寫為short)占用的存儲空間可能比int類型少,常用于較小數(shù)值的場合以節(jié)省空間。與int類似,short是有符號類型。

?long int或long占用的存儲空間可能比int多,適用于較大數(shù)值的場合。與int類似,long是有符號類型。

?long long int或long long(C99標(biāo)準(zhǔn)加入)占用的儲存空間可能比long多,適用于更大數(shù)值的場合。該類型至少占64位。與int類似,long long是有符號類型。

?unsigned int或unsigned只用于非負(fù)值的場合。這種類型與有符號類型表示的范圍不同。例如,16位unsigned int允許的取值范圍是0~65535,而不是-32768~32767。用于表示正負(fù)號的位現(xiàn)在用于表示另一個二進(jìn)制位,所以無符號整型可以表示更大的數(shù)。

?在C90標(biāo)準(zhǔn)中,添加了unsigned long int或unsigned long和unsigned int或unsigned short類型。C99標(biāo)準(zhǔn)又添加了unsigned long long int或unsigned longlong。

?在任何有符號類型前面添加關(guān)鍵字signed,可強(qiáng)調(diào)使用有符號類型的意圖。例如,short、short int、signed short、signed short int都表示同一種類型。

1.聲明其他整數(shù)類型

其他整數(shù)類型的聲明方式與int類型相同,下面列出了一些例子。不是所有的C編譯器都能識別最后3條聲明,最后一個例子所有的類型是C99標(biāo)準(zhǔn)新增的。

long int estine;

long johns;

short int erns;

short ribs;

unsigned int s_count;

unsigned players;

unsigned long headcount;

unsigned short yesvotes;

long long ago;

2.使用多種整數(shù)類型的原因

為什么說short類型“可能”比int類型占用的空間少,long類型“可能”比int類型占用的空間多?因?yàn)镃語言只規(guī)定了short占用的存儲空間不能多于int,long占用的存儲空間不能少于int。這樣規(guī)定是為了適應(yīng)不同的機(jī)器。例如,過去的一臺運(yùn)行Windows 3的機(jī)器上,int類型和short類型都占16位,long類型占32位。后來,Windows和蘋果系統(tǒng)都使用16位儲存short類型,32位儲存int類型和long類型(使用32位可以表示的整數(shù)數(shù)值超過20億)。現(xiàn)在,計(jì)算機(jī)普遍使用64位處理器,為了儲存64位的整數(shù),才引入了long long類型。

現(xiàn)在,個人計(jì)算機(jī)上最常見的設(shè)置是,long long占64位,long占32位,short占16位,int占16位或32位(依計(jì)算機(jī)的自然字長而定)。原則上,這4種類型代表4種不同的大小,但是在實(shí)際使用中,有些類型之間通常有重疊。

C 標(biāo)準(zhǔn)對基本數(shù)據(jù)類型只規(guī)定了允許的最小大小。對于 16 位機(jī),short和 int 的最小取值范圍是[?32767,32767];對于32位機(jī),long的最小取值范圍是[?2147483647,2147483647]。對于unsigned short和unsigned int,最小取值范圍是[0,65535];對于unsigned long,最小取值范圍是[0,4294967295]。long long類型是為了支持64位的需求,最小取值范圍是[?9223372036854775807,9223372036854775807];unsigned long long的最小取值范圍是[0,18446744073709551615]。如果要開支票,這個數(shù)是一千八百億億(兆)六千七百四十四萬億零七百三十七億零九百五十五萬一千六百一十五。但是,誰會去數(shù)?

int類型那么多,應(yīng)該如何選擇?首先,考慮unsigned類型。這種類型的數(shù)常用于計(jì)數(shù),因?yàn)橛?jì)數(shù)不用負(fù)數(shù)。而且,unsigned類型可以表示更大的正數(shù)。

如果一個數(shù)超出了int類型的取值范圍,且在long類型的取值范圍內(nèi)時(shí),使用long類型。然而,對于那些long占用的空間比int大的系統(tǒng),使用long類型會減慢運(yùn)算速度。因此,如非必要,請不要使用long類型。另外要注意一點(diǎn):如果在long類型和int類型占用空間相同的機(jī)器上編寫代碼,當(dāng)確實(shí)需要32位的整數(shù)時(shí),應(yīng)使用long類型而不是int類型,以便把程序移植到16位機(jī)后仍然可以正常工作。類似地,如果確實(shí)需要64位的整數(shù),應(yīng)使用long long類型。

如果在int設(shè)置為32位的系統(tǒng)中要使用16位的值,應(yīng)使用short類型以節(jié)省存儲空間。通常,只有當(dāng)程序使用相對于系統(tǒng)可用內(nèi)存較大的整型數(shù)組時(shí),才需要重點(diǎn)考慮節(jié)省空間的問題。使用short類型的另一個原因是,計(jì)算機(jī)中某些組件使用的硬件寄存器是16位。

3.long常量和long long常量

通常,程序代碼中使用的數(shù)字(如,2345)都被儲存為int類型。如果使用1000000這樣的大數(shù)字,超出了int類型能表示的范圍,編譯器會將其視為long int類型(假設(shè)這種類型可以表示該數(shù)字)。如果數(shù)字超出long可表示的最大值,編譯器則將其視為unsigned long類型。如果還不夠大,編譯器則將其視為long long或unsigned long long類型(前提是編譯器能識別這些類型)。

八進(jìn)制和十六進(jìn)制常量被視為int類型。如果值太大,編譯器會嘗試使用unsigned int。如果還不夠大,編譯器會依次使用long、unsigned long、long long和unsigned long long類型。

有些情況下,需要編譯器以long類型儲存一個小數(shù)字。例如,編程時(shí)要顯式使用IBM PC上的內(nèi)存地址時(shí)。另外,一些C標(biāo)準(zhǔn)函數(shù)也要求使用long類型的值。要把一個較小的常量作為long類型對待,可以在值的末尾加上l(小寫的L)或L后綴。使用L后綴更好,因?yàn)閘看上去和數(shù)字1很像。因此,在int為16位、long為32位的系統(tǒng)中,會把7作為16位儲存,把7L作為32位儲存。l或L后綴也可用于八進(jìn)制和十六進(jìn)制整數(shù),如020L和0x10L。

類似地,在支持long long類型的系統(tǒng)中,也可以使用ll或LL后綴來表示long long類型的值,如3LL。另外,u或U后綴表示unsigned long long,如5ull、10LLU、6LLU或9Ull。

整數(shù)溢出

如果整數(shù)超出了相應(yīng)類型的取值范圍會怎樣?下面分別將有符號類型和無符號類型的整數(shù)設(shè)置為比最大值略大,看看會發(fā)生什么(printf()函數(shù)使用%u說明顯示unsigned int類型的值)。

/* toobig.c-- 超出系統(tǒng)允許的最大int值*/

#include <stdio.h>

int main(void)

{

? ? int i = 2147483647;

? ? unsigned int j = 4294967295;

? ? printf("%d %d %d\n", i, i+1, i+2);

? ? printf("%u %u %u\n", j, j+1, j+2);

? ? return 0;

}

在我們的系統(tǒng)下輸出的結(jié)果是:

2147483647 -2147483648 -2147483647

4294967295 0 1

可以把無符號整數(shù)j看作是汽車的里程表。當(dāng)達(dá)到它能表示的最大值時(shí),會重新從起始點(diǎn)開始。整數(shù) i 也是類似的情況。它們主要的區(qū)別是,在超過最大值時(shí),unsigned int 類型的變量 j 從 0開始;而int類型的變量i則從?2147483648開始。注意,當(dāng)i超出(溢出)其相應(yīng)類型所能表示的最大值時(shí),系統(tǒng)并未通知用戶。因此,在編程時(shí)必須自己注意這類問題。

溢出行為是未定義的行為,C 標(biāo)準(zhǔn)并未定義有符號類型的溢出規(guī)則。以上描述的溢出行為比較有代表性,但是也可能會出現(xiàn)其他情況。

4.打印short、long、long long和unsigned類型

打印unsigned int類型的值,使用%u轉(zhuǎn)換說明;打印long類型的值,使用%ld轉(zhuǎn)換說明。如果系統(tǒng)中int和long的大小相同,使用%d就行。但是,這樣的程序被移植到其他系統(tǒng)(int和long類型的大小不同)中會無法正常工作。在x和o前面可以使用l前綴,%lx表示以十六進(jìn)制格式打印long類型整數(shù),%lo表示以八進(jìn)制格式打印long類型整數(shù)。注意,雖然C允許使用大寫或小寫的常量后綴,但是在轉(zhuǎn)換說明中只能用小寫。

C語言有多種printf()格式。對于short類型,可以使用h前綴。%hd表示以十進(jìn)制顯示short類型的整數(shù),%ho表示以八進(jìn)制顯示short類型的整數(shù)。h和l前綴都可以和u一起使用,用于表示無符號類型。例如,%lu表示打印unsigned long類型的值。程序清單3.4演示了一些例子。對于支持long long類型的系統(tǒng),%lld和%llu分別表示有符號和無符號類型。第4章將詳細(xì)介紹轉(zhuǎn)換說明。

程序清單3.4 print2.c程序

/* print2.c--更多printf()的特性 */

#include <stdio.h>

int main(void)

{

? ? unsigned int un = 3000000000; /* int為32位和short為16位的系統(tǒng) */

? ? short end = 200;

? ? long big = 65537;

? ? long long verybig = 12345678908642;

? ? printf("un = %u and not %d\n", un, un);

? ? printf("end = %hd and %d\n", end, end);

? ? printf("big = %ld and not %hd\n", big, big);

? ? printf("verybig= %lld and not %ld\n", verybig, verybig);

? ? return 0;

}

在特定的系統(tǒng)中輸出如下(輸出的結(jié)果可能不同):

un = 3000000000 and not -1294967296

end = 200 and 200

big = 65537 and not 1

verybig= 12345678908642 and not 1942899938

該例表明,使用錯誤的轉(zhuǎn)換說明會得到意想不到的結(jié)果。第 1 行輸出,對于無符號變量 un,使用%d會生成負(fù)值!其原因是,無符號值 3000000000和有符號值?129496296 在系統(tǒng)內(nèi)存中的內(nèi)部表示完全相同(詳見第15章)。因此,如果告訴printf()該數(shù)是無符號數(shù),它打印一個值;如果告訴它該數(shù)是有符號數(shù),它將打印另一個值。在待打印的值大于有符號值的最大值時(shí),會發(fā)生這種情況。對于較小的正數(shù)(如96),有符號和無符號類型的存儲、顯示都相同。

第2行輸出,對于short類型的變量end,在printf()中無論指定以short類型(%hd)還是int類型(%d)打印,打印出來的值都相同。這是因?yàn)樵诮o函數(shù)傳遞參數(shù)時(shí),C編譯器把short類型的值自動轉(zhuǎn)換成int類型的值。你可能會提出疑問:為什么要進(jìn)行轉(zhuǎn)換?h修飾符有什么用?第1個問題的答案是,int類型被認(rèn)為是計(jì)算機(jī)處理整數(shù)類型時(shí)最高效的類型。因此,在short和int類型的大小不同的計(jì)算機(jī)中,用int類型的參數(shù)傳遞速度更快。第2個問題的答案是,使用h修飾符可以顯示較大整數(shù)被截?cái)喑?short 類型值的情況。第 3 行輸出就演示了這種情況。把 65537以二進(jìn)制格式寫成一個 32 位數(shù)是00000000000000010000000000000001。使用%hd,printf()只會查看后 16位,所以顯示的值是 1。與此類似,輸出的最后一行先顯示了verybig的完整值,然后由于使用了%ld,printf()只顯示了儲存在后32位的值。

本章前面介紹過,程序員必須確保轉(zhuǎn)換說明的數(shù)量和待打印值的數(shù)量相同。以上內(nèi)容也提醒讀者,程序員還必須根據(jù)待打印值的類型使用正確的轉(zhuǎn)換說明。

提示 匹配printf()說明符的類型

在使用 printf()函數(shù)時(shí),切記檢查每個待打印值都有對應(yīng)的轉(zhuǎn)換說明,還要檢查轉(zhuǎn)換說明的類型是否與待打印值的類型相匹配。

3.4.3 使用字符:char類型

char類型用于儲存字符(如,字母或標(biāo)點(diǎn)符號),但是從技術(shù)層面看,char是整數(shù)類型。因?yàn)閏har類型實(shí)際上儲存的是整數(shù)而不是字符。計(jì)算機(jī)使用數(shù)字編碼來處理字符,即用特定的整數(shù)表示特定的字符。美國最常用的編碼是ASCII編碼,本書也使用此編碼。例如,在ASCII碼中,整數(shù)65代表大寫字母A。因此,儲存字母A實(shí)際上儲存的是整數(shù)65(許多IBM的大型主機(jī)使用另一種編碼——EBCDIC,其原理相同。另外,其他國家的計(jì)算機(jī)系統(tǒng)可能使用完全不同的編碼)。

標(biāo)準(zhǔn)ASCII碼的范圍是0~127,只需7位二進(jìn)制數(shù)即可表示。通常,char類型被定義為8位的存儲單元,因此容納標(biāo)準(zhǔn)ASCII碼綽綽有余。許多其他系統(tǒng)(如IMB PC和蘋果Macs)還提供擴(kuò)展ASCII碼,也在8位的表示范圍之內(nèi)。一般而言,C語言會保證char類型足夠大,以儲存系統(tǒng)(實(shí)現(xiàn)C語言的系統(tǒng))的基本字符集。

許多字符集都超過了127,甚至多于255。例如,日本漢字(kanji)字符集。商用的統(tǒng)一碼(Unicode)創(chuàng)建了一個能表示世界范圍內(nèi)多種字符集的系統(tǒng),目前包含的字符已超過110000個。國際標(biāo)準(zhǔn)化組織(ISO)和國際電工技術(shù)委員會(IEC)為字符集開發(fā)了ISO/IEC 10646標(biāo)準(zhǔn)。統(tǒng)一碼標(biāo)準(zhǔn)也與ISO/IEC 10646標(biāo)準(zhǔn)兼容。

C語言把1字節(jié)定義為char類型占用的位(bit)數(shù),因此無論是16位還是32位系統(tǒng),都可以使用char類型。

1.聲明char類型變量

char類型變量的聲明方式與其他類型變量的聲明方式相同。下面是一些例子:

char response;

char itable, latan;

以上聲明創(chuàng)建了3個char類型的變量:response、itable和latan。

2.字符常量和初始化

如果要把一個字符常量初始化為字母 A,不必背下 ASCII 碼,用計(jì)算機(jī)語言很容易做到。通過以下初始化把字母A賦給grade即可:

char grade = 'A';

在C語言中,用單引號括起來的單個字符被稱為字符常量(characterconstant)。編譯器一發(fā)現(xiàn)'A',就會將其轉(zhuǎn)換成相應(yīng)的代碼值。單引號必不可少。下面還有一些其他的例子:

char broiled; /* 聲明一個char類型的變量 */

broiled = 'T'; /* 為其賦值,正確 */

broiled = T; /* 錯誤!此時(shí)T是一個變量 */

broiled = "T"; /* 錯誤!此時(shí)"T"是一個字符串 */

如上所示,如果省略單引號,編譯器認(rèn)為T是一個變量名;如果把T用雙引號括起來,編譯器則認(rèn)為"T"是一個字符串。字符串的內(nèi)容將在第4章中介紹。

實(shí)際上,字符是以數(shù)值形式儲存的,所以也可使用數(shù)字代碼值來賦值:

char grade = 65; /* 對于ASCII,這樣做沒問題,但這是一種不好的編程風(fēng)格 */

在本例中,雖然65是int類型,但是它在char類型能表示的范圍內(nèi),所以將其賦值給grade沒問題。由于65是字母A對應(yīng)的ASCII碼,因此本例是把A賦給grade。注意,能這樣做的前提是系統(tǒng)使用ASCII碼。其實(shí),用'A'代替65才是較為妥當(dāng)?shù)淖龇?,這樣在任何系統(tǒng)中都不會出問題。因此,最好使用字符常量,而不是數(shù)字代碼值。

奇怪的是,C語言將字符常量視為int類型而非char類型。例如,在int為32位、char為8位的ASCII系統(tǒng)中,有下面的代碼:

char grade = 'B';

本來'B'對應(yīng)的數(shù)值66儲存在32位的存儲單元中,現(xiàn)在卻可以儲存在8位的存儲單元中(grade)。利用字符常量的這種特性,可以定義一個字符常量'FATE',即把4個獨(dú)立的8位ASCII碼儲存在一個32位存儲單元中。如果把這樣的字符常量賦給char類型變量grade,只有最后8位有效。因此,grade的值是'E'。

3.非打印字符

單引號只適用于字符、數(shù)字和標(biāo)點(diǎn)符號,瀏覽ASCII表會發(fā)現(xiàn),有些ASCII字符打印不出來。例如,一些代表行為的字符(如,退格、換行、終端響鈴或蜂鳴)。C語言提供了3種方法表示這些字符。

第1種方法前面介紹過——使用ASCII碼。例如,蜂鳴字符的ASCII值是7,因此可以這樣寫:

char beep = 7;

第 2 種方法是,使用特殊的符號序列表示一些特殊的字符。這些符號序列叫作轉(zhuǎn)義序列(escape sequence)。表3.2列出了轉(zhuǎn)義序列及其含義。

把轉(zhuǎn)義序列賦給字符變量時(shí),必須用單引號把轉(zhuǎn)義序列括起來。例如,假設(shè)有下面一行代碼:

char nerf = '\n';

稍后打印變量nerf的效果是,在打印機(jī)或屏幕上另起一行。

表3.2? 轉(zhuǎn)義序列

?

現(xiàn)在,我們來仔細(xì)分析一下轉(zhuǎn)義序列。使用C90新增的警報(bào)字符(\a)是否能產(chǎn)生聽到或看到的警報(bào),取決于計(jì)算機(jī)的硬件,蜂鳴是最常見的警報(bào)(在一些系統(tǒng)中,警報(bào)字符不起作用)。C標(biāo)準(zhǔn)規(guī)定警報(bào)字符不得改變活躍位置。標(biāo)準(zhǔn)中的活躍位置(active position)指的是顯示設(shè)備(屏幕、電傳打字機(jī)、打印機(jī)等)中下一個字符將出現(xiàn)的位置。簡而言之,平時(shí)常說的屏幕光標(biāo)位置就是活躍位置。在程序中把警報(bào)字符輸出在屏幕上的效果是,發(fā)出一聲蜂鳴,但不會移動屏幕光標(biāo)。

接下來的轉(zhuǎn)義字符\b、\f、\n、\r、\t和\v是常用的輸出設(shè)備控制字符。了解它們最好的方式是查看它們對活躍位置的影響。換頁符(\f)把活躍位置移至下一頁的開始處;換行符(\n)把活躍位置移至下一行的開始處;回車符(\r)把活躍位置移動到當(dāng)前行的開始處;水平制表符(\t)將活躍位置移至下一個水平制表點(diǎn)(通常是第1個、第9個、第17個、第25個等字符位置);垂直制表符(\v)把活躍位置移至下一個垂直制表點(diǎn)。

這些轉(zhuǎn)義序列字符不一定在所有的顯示設(shè)備上都起作用。例如,換頁符和垂直制表符在PC屏幕上會生成奇怪的符號,光標(biāo)并不會移動。只有將其輸出到打印機(jī)上時(shí)才會產(chǎn)生前面描述的效果。

接下來的3個轉(zhuǎn)義序列(\\、\'、\")用于打印\、'、"字符(由于這些字符用于定義字符常量,是printf()函數(shù)的一部分,若直接使用它們會造成混亂)。如果打印下面一行內(nèi)容:

Gramps sez, "a \ is a backslash."

應(yīng)這樣編寫代碼:

printf("Gramps sez, \"a \\ is a backslash.\"\n");

表3.2中的最后兩個轉(zhuǎn)義序列(\0oo和\xhh)是ASCII碼的特殊表示。如果要用八進(jìn)制ASCII碼表示一個字符,可以在編碼值前面加一個反斜杠(\)并用單引號括起來。例如,如果編譯器不識別警報(bào)字符(\a),可以使用ASCII碼來代替:

beep = '\007';

可以省略前面的 0,'\07'甚至'\7'都可以。即使沒有前綴 0,編譯器在處理這種寫法時(shí),仍會解釋為八進(jìn)制。

從C90開始,不僅可以用十進(jìn)制、八進(jìn)制形式表示字符常量,C語言還提供了第3種選擇——用十六進(jìn)制形式表示字符常量,即反斜杠后面跟一個x或X,再加上1~3位十六進(jìn)制數(shù)字。例如,Ctrl+P字符的ASCII十六進(jìn)制碼是10(相當(dāng)于十進(jìn)制的16),可表示為'\x10'或'\x010'。圖3.5列出了一些整數(shù)類型的不同進(jìn)制形式。

?

使用ASCII碼時(shí),注意數(shù)字和數(shù)字字符的區(qū)別。例如,字符4對應(yīng)的ASCII碼是52。'4'表示字符4,而不是數(shù)值4。

關(guān)于轉(zhuǎn)義序列,讀者可能有下面3個問題。

?上面最后一個例子(printf("Gramps sez, \"a \\ is a backslash\"\"n"),為何沒有用單引號把轉(zhuǎn)義序列括起來?無論是普通字符還是轉(zhuǎn)義序列,只要是雙引號括起來的字符集合,就無需用單引號括起來。雙引號中的字符集合叫作字符串(詳見第4章)。注意,該例中的其他字符(G、r、a、m、p、s等)都沒有用單引號括起來。與此類似,printf("Hello!\007\n");將打印Hello!并發(fā)出一聲蜂鳴,而 printf("Hello!7\n");則打印 Hello!7。不是轉(zhuǎn)義序列中的數(shù)字將作為普通字符被打印出來。

?何時(shí)使用ASCII碼?何時(shí)使用轉(zhuǎn)義序列?如果要在轉(zhuǎn)義序列(假設(shè)使用'\f')和ASCII碼('\014')之間選擇,請選擇前者(即'\f')。這樣的寫法不僅更好記,而且可移植性更高。'\f'在不使用ASCII碼的系統(tǒng)中,仍然有效。

?如果要使用ASCII碼,為何要寫成'\032'而不是032?首先,'\032'能更清晰地表達(dá)程序員使用字符編碼的意圖。其次,類似\032這樣的轉(zhuǎn)義序列可以嵌入C的字符串中,如printf("Hello!\007\n");中就嵌入了\007。

4.打印字符

printf()函數(shù)用%c指明待打印的字符。前面介紹過,一個字符變量實(shí)際上被儲存為1字節(jié)的整數(shù)值。因此,如果用%d轉(zhuǎn)換說明打印 char類型變量的值,打印的是一個整數(shù)。而%c轉(zhuǎn)換說明告訴printf()打印該整數(shù)值對應(yīng)的字符。程序清單3.5演示了打印char類型變量的兩種方式。

程序清單3.5 charcode.c程序

/* charcode.c-顯示字符的代碼編號 */

#include <stdio.h>

int main(void)

{

? ? char ch;

? ? printf("Please enter a character.\n");

? ? scanf("%c", &ch); /* 用戶輸入字符 */

? ? printf("The code for %c is %d.\n", ch, ch);

? ? return 0;

}

運(yùn)行該程序后,輸出示例如下:

Please enter a character.

C

The code for C is 67.

運(yùn)行該程序時(shí),在輸入字母后不要忘記按下Enter或Return鍵。隨后,scanf()函數(shù)會讀取用戶輸入的字符,&符號表示把輸入的字符賦給變量ch。接著,printf()函數(shù)打印ch的值兩次,第1次打印一個字符(對應(yīng)代碼中的%c),第2次打印一個十進(jìn)制整數(shù)值(對應(yīng)代碼中的%d)。注意,printf()函數(shù)中的轉(zhuǎn)換說明決定了數(shù)據(jù)的顯示方式,而不是數(shù)據(jù)的儲存方式(見圖3.6)。

5.有符號還是無符號

有些C編譯器把char實(shí)現(xiàn)為有符號類型,這意味著char可表示的范圍是-128~127。而有些C編譯器把char實(shí)現(xiàn)為無符號類型,那么char可表示的范圍是0~255。請查閱相應(yīng)的編譯器手冊,確定正在使用的編譯器如何實(shí)現(xiàn)char類型。或者,可以查閱limits.h頭文件。下一章將詳細(xì)介紹頭文件的內(nèi)容。

根據(jù)C90標(biāo)準(zhǔn),C語言允許在關(guān)鍵字char前面使用signed或unsigned。這樣,無論編譯器默認(rèn)char是什么類型,signed char表示有符號類型,而unsigned char表示無符號類型。這在用char類型處理小整數(shù)時(shí)很有用。如果只用char處理字符,那么char前面無需使用任何修飾符。

3.4.4 _Bool類型

C99標(biāo)準(zhǔn)添加了_Bool類型,用于表示布爾值,即邏輯值true和false。因?yàn)镃語言用值1表示true,值0表示false,所以_Bool類型實(shí)際上也是一種整數(shù)類型。但原則上它僅占用1位存儲空間,因?yàn)閷?和1而言,1位的存儲空間足夠了。

程序通過布爾值可選擇執(zhí)行哪部分代碼。我們將在第6章和第7章中詳述相關(guān)內(nèi)容。

3.4.5 可移植類型:stdint.h和inttypes.h

C 語言提供了許多有用的整數(shù)類型。但是,某些類型名在不同系統(tǒng)中的功能不一樣。C99 新增了兩個頭文件stdint.h和inttypes.h,以確保C語言的類型在各系統(tǒng)中的功能相同。

C語言為現(xiàn)有類型創(chuàng)建了更多類型名。這些新的類型名定義在stdint.h頭文件中。例如,int32_t表示32位的有符號整數(shù)類型。在使用32位int的系統(tǒng)中,頭文件會把int32_t作為int的別名。不同的系統(tǒng)也可以定義相同的類型名。例如,int為16位、long為32位的系統(tǒng)會把int32_t作為long的別名。然后,使用int32_t類型編寫程序,并包含stdint.h頭文件時(shí),編譯器會把int或long替換成與當(dāng)前系統(tǒng)匹配的類型。

上面討論的類型別名是精確寬度整數(shù)類型(exact-width integer type)的示例。int32_t表示整數(shù)類型的寬度正好是32位。但是,計(jì)算機(jī)的底層系統(tǒng)可能不支持。因此,精確寬度整數(shù)類型是可選項(xiàng)。

如果系統(tǒng)不支持精確寬度整數(shù)類型怎么辦?C99和C11提供了第2類別名集合。一些類型名保證所表示的類型一定是至少有指定寬度的最小整數(shù)類型。這組類型集合被稱為最小寬度類型(minimum width type)。例如,int_least8_t是可容納8位有符號整數(shù)值的類型中寬度最小的類型的一個別名。如果某系統(tǒng)的最小整數(shù)類型是16位,可能不會定義int8_t類型。盡管如此,該系統(tǒng)仍可使用int_least8_t類型,但可能把該類型實(shí)現(xiàn)為16位的整數(shù)類型。

當(dāng)然,一些程序員更關(guān)心速度而非空間。為此,C99和C11定義了一組可使計(jì)算達(dá)到最快的類型集合。這組類型集合被稱為最快最小寬度類型(fastst minimum width type)。例如,int_fast8_t被定義為系統(tǒng)中對8位有符號值而言運(yùn)算最快的整數(shù)類型的別名。

另外,有些程序員需要系統(tǒng)的最大整數(shù)類型。為此,C99定義了最大的有符號整數(shù)類型intmax_t,可儲存任何有效的有符號整數(shù)值。類似地,unitmax_t表示最大的無符號整數(shù)類型。順帶一提,這些類型有可能比long long和unsigned long類型更大,因?yàn)镃編譯器除了實(shí)現(xiàn)標(biāo)準(zhǔn)規(guī)定的類型以外,還可利用C語言實(shí)現(xiàn)其他類型。例如,一些編譯器在標(biāo)準(zhǔn)引入 long long 類型之前,已提前實(shí)現(xiàn)了該類型。

C99 和 C11 不僅提供可移植的類型名,還提供相應(yīng)的輸入和輸出。例如,printf()打印特定類型時(shí)要求與相應(yīng)的轉(zhuǎn)換說明匹配。如果要打印int32_t類型的值,有些定義使用%d,而有些定義使用%ld,怎么辦?C 標(biāo)準(zhǔn)針對這一情況,提供了一些字符串宏(第 4 章中詳細(xì)介紹)來顯示可移植類型。例如, inttypes.h頭文件中定義了PRId32字符串宏,代表打印32位有符號值的合適轉(zhuǎn)換說明(如d或l)。程序清單3.6演示了一種可移植類型和相應(yīng)轉(zhuǎn)換說明的用法。

程序清單3.6 altnames.c程序

/* altnames.c -- 可移植整數(shù)類型名 */

#include <stdio.h>

#include <inttypes.h> // 支持可移植類型

?

int main(void)

{

? ? int32_t me32; // me32是一個32位有符號整型變量

? ? me32 = 45933945;

? ? printf("First, assume int32_t is int: ");

? ? printf("me32 = %d\n", me32);

? ? printf("Next, let's not make any assumptions.\n");

? ? printf("Instead, use a \"macro\" from inttypes.h: ");

? ? printf("me32 = %" PRId32 "\n", me32);

? ? return 0;

}

該程序最后一個printf()中,參數(shù)PRId32被定義在inttypes.h中的"d"替換,因而這條語句等價(jià)于:

printf("me16 = %" "d" "\n", me16);

在C語言中,可以把多個連續(xù)的字符串組合成一個字符串,所以這條語句又等價(jià)于:

printf("me16 = %d\n", me16);

下面是該程序的輸出,注意,程序中使用了\"轉(zhuǎn)義序列來顯示雙引號:

First, assume int32_t is int: me32 = 45933945

Next, let's not make any assumptions.

Instead, use a "macro" from inttypes.h: me32 = 45933945

篇幅有限,無法介紹擴(kuò)展的所有整數(shù)類型。本節(jié)主要是為了讓讀者知道,在需要時(shí)可進(jìn)行這種級別的類型控制。附錄B中的參考資料VI“擴(kuò)展的整數(shù)類型”介紹了完整的inttypes.h和stdint.h頭文件。

注意 對C99/C11的支持

C語言發(fā)展至今,雖然ISO已發(fā)布了C11標(biāo)準(zhǔn),但是編譯器供應(yīng)商對C99的實(shí)現(xiàn)程度卻各不相同。在本書第6版的編寫過程中,一些編譯器仍未實(shí)現(xiàn)inttypes.h頭文件及其相關(guān)功能。

3.4.6 float、double和long double

各種整數(shù)類型對大多數(shù)軟件開發(fā)項(xiàng)目而言夠用了。然而,面向金融和數(shù)學(xué)的程序經(jīng)常使用浮點(diǎn)數(shù)。C語言中的浮點(diǎn)類型有float、double和long double類型。它們與FORTRAN和Pascal中的real類型一致。前面提到過,浮點(diǎn)類型能表示包括小數(shù)在內(nèi)更大范圍的數(shù)。浮點(diǎn)數(shù)的表示類似于科學(xué)記數(shù)法(即用小數(shù)乘以10的冪來表示數(shù)字)。該記數(shù)系統(tǒng)常用于表示非常大或非常小的數(shù)。表3.3列出了一些示例。

第1列是一般記數(shù)法;第2列是科學(xué)記數(shù)法;第3列是指數(shù)記數(shù)法(或稱為e記數(shù)法),這是科學(xué)記數(shù)法在計(jì)算機(jī)中的寫法,e后面的數(shù)字代表10的指數(shù)。圖3.7演示了更多的浮點(diǎn)數(shù)寫法。

C標(biāo)準(zhǔn)規(guī)定,float類型必須至少能表示6位有效數(shù)字,且取值范圍至少是10^-37~10^+37。前一項(xiàng)規(guī)定指float類型必須至少精確表示小數(shù)點(diǎn)后的6位有效數(shù)字,如33.333333。后一項(xiàng)規(guī)定用于方便地表示諸如太陽質(zhì)量(2.0e30千克)、一個質(zhì)子的電荷量(1.6e-19庫侖)或國家債務(wù)之類的數(shù)字。通常,系統(tǒng)儲存一個浮點(diǎn)數(shù)要占用32位。其中8位用于表示指數(shù)的值和符號,剩下24位用于表示非指數(shù)部分(也叫作尾數(shù)或有效數(shù))及其符號。

C語言提供的另一種浮點(diǎn)類型是double(意為雙精度)。double類型和float類型的最小取值范圍相同,但至少必須能表示10位有效數(shù)字。一般情況下,double占用64位而不是32位。一些系統(tǒng)將多出的 32 位全部用來表示非指數(shù)部分,這不僅增加了有效數(shù)字的位數(shù)(即提高了精度),而且還減少了舍入誤差。另一些系統(tǒng)把其中的一些位分配給指數(shù)部分,以容納更大的指數(shù),從而增加了可表示數(shù)的范圍。無論哪種方法,double類型的值至少有13位有效數(shù)字,超過了標(biāo)準(zhǔn)的最低位數(shù)規(guī)定。C語言的第3種浮點(diǎn)類型是long double,以滿足比double類型更高的精度要求。不過,C只保證long double類型至少與double類型的精度相同。

1.聲明浮點(diǎn)型變量

浮點(diǎn)型變量的聲明和初始化方式與整型變量相同,下面是一些例子:

float noah, jonah;

double trouble;

float planck = 6.63e-34;

long double gnp;

2.浮點(diǎn)型常量

在代碼中,可以用多種形式書寫浮點(diǎn)型常量。浮點(diǎn)型常量的基本形式是:有符號的數(shù)字(包括小數(shù)點(diǎn)),后面緊跟e或E,最后是一個有符號數(shù)表示10的指數(shù)。下面是兩個有效的浮點(diǎn)型常量:

-1.56E+12

2.87e-3

正號可以省略??梢詻]有小數(shù)點(diǎn)(如,2E5)或指數(shù)部分(如,19.28),但是不能同時(shí)省略兩者??梢允÷孕?shù)部分(如,3.E16)或整數(shù)部分(如,.45E-6),但是不能同時(shí)省略兩者。下面是更多的有效浮點(diǎn)型常量示例:

3.14159

.2

4e16

.8E-5

100.

?

不要在浮點(diǎn)型常量中間加空格:1.56 E+12(錯誤?。?/p>

默認(rèn)情況下,編譯器假定浮點(diǎn)型常量是double類型的精度。例如,假設(shè)some是float類型的變量,編寫下面的語句:

some = 4.0 * 2.0;

通常,4.0和2.0被儲存為64位的double類型,使用雙精度進(jìn)行乘法運(yùn)算,然后將乘積截?cái)喑蒮loat類型的寬度。這樣做雖然計(jì)算精度更高,但是會減慢程序的運(yùn)行速度。

在浮點(diǎn)數(shù)后面加上f或F后綴可覆蓋默認(rèn)設(shè)置,編譯器會將浮點(diǎn)型常量看作float類型,如2.3f和9.11E9F。使用l或L后綴使得數(shù)字成為long double類型,如54.3l和4.32L。注意,建議使用L后綴,因?yàn)樽帜竘和數(shù)字1很容易混淆。沒有后綴的浮點(diǎn)型常量是double類型。

C99 標(biāo)準(zhǔn)添加了一種新的浮點(diǎn)型常量格式——用十六進(jìn)制表示浮點(diǎn)型常量,即在十六進(jìn)制數(shù)前加上十六進(jìn)制前綴(0x或0X),用p和P分別代替e和E,用2的冪代替10的冪(即,p計(jì)數(shù)法)。如下所示:

0xa.1fp10

十六進(jìn)制a等于十進(jìn)制10,.1f是1/16加上15/256(十六進(jìn)制f等于十進(jìn)制15),p10是2^10或1024。0xa.1fp10表示的值是(10 + 1/16 +15/256)×1024(即,十進(jìn)制10364.0)。

注意,并非所有的編譯器都支持C99的這一特性。

3.打印浮點(diǎn)值

printf()函數(shù)使用%f轉(zhuǎn)換說明打印十進(jìn)制記數(shù)法的float和double類型浮點(diǎn)數(shù),用%e打印指數(shù)記數(shù)法的浮點(diǎn)數(shù)。如果系統(tǒng)支持十六進(jìn)制格式的浮點(diǎn)數(shù),可用a和A分別代替e和E。打印long double類型要使用%Lf、%Le或%La轉(zhuǎn)換說明。給那些未在函數(shù)原型中顯式說明參數(shù)類型的函數(shù)(如,printf())傳遞參數(shù)時(shí),C編譯器會把float類型的值自動轉(zhuǎn)換成double類型。程序清單3.7演示了這些特性。

程序清單3.7 showf_pt.c程序

/* showf_pt.c -- 以兩種方式顯示float類型的值 */

#include <stdio.h>

int main(void)

{

? ? float aboat = 32000.0;

? ? double abet = 2.14e9;

? ? long double dip = 5.32e-5;

? ? printf("%f can be written %e\n", aboat, aboat);

? ? // 下一行要求編譯器支持C99或其中的相關(guān)特性

? ? printf("And it's %a in hexadecimal, powers of 2 notation\n",aboat);

? ? printf("%f can be written %e\n", abet, abet);

? ? printf("%Lf can be written %Le\n", dip, dip);

? ? return 0;

}

該程序的輸出如下,前提是編譯器支持C99/C11:

32000.000000 can be written 3.200000e+04

And it's 0x1.f4p+14 in hexadecimal, powers of 2 notation

2140000000.000000 can be written 2.140000e+09

0.000053 can be written 5.320000e-05

該程序示例演示了默認(rèn)的輸出效果。下一章將介紹如何通過設(shè)置字段寬度和小數(shù)位數(shù)來控制輸出格式。

4.浮點(diǎn)值的上溢和下溢

假設(shè)系統(tǒng)的最大float類型值是3.4E38,編寫如下代碼:

float toobig = 3.4E38 * 100.0f;

printf("%e\n", toobig);

會發(fā)生什么?這是一個上溢(overflow)的示例。當(dāng)計(jì)算導(dǎo)致數(shù)字過大,超過當(dāng)前類型能表達(dá)的范圍時(shí),就會發(fā)生上溢。這種行為在過去是未定義的,不過現(xiàn)在C語言規(guī)定,在這種情況下會給toobig賦一個表示無窮大的特定值,而且printf()顯示該值為inf或infinity(或者具有無窮含義的其他內(nèi)容)。

當(dāng)除以一個很小的數(shù)時(shí),情況更為復(fù)雜?;貞浺幌?,float類型的數(shù)以指數(shù)和尾數(shù)部分來儲存。存在這樣一個數(shù),它的指數(shù)部分是最小值,即由全部可用位表示的最小尾數(shù)值。該數(shù)字是float類型能用全部精度表示的最小數(shù)字。現(xiàn)在把它除以 2。通常,這個操作會減小指數(shù)部分,但是假設(shè)的情況中,指數(shù)已經(jīng)是最小值了。所以計(jì)算機(jī)只好把尾數(shù)部分的位向右移,空出第1個二進(jìn)制位,并丟棄最后一個二進(jìn)制數(shù)。以十進(jìn)制為例,把一個有4位有效數(shù)字的數(shù)(如,0.1234E-10)除以10,得到的結(jié)果是0.0123E-10。雖然得到了結(jié)果,但是在計(jì)算過程中卻損失了原末尾有效位上的數(shù)字。這種情況叫作下溢(underflow)。C語言把損失了類型全精度的浮點(diǎn)值稱為低于正常的(subnormal)浮點(diǎn)值。因此,把最小的正浮點(diǎn)數(shù)除以 2將得到一個低于正常的值。如果除以一個非常大的值,會導(dǎo)致所有的位都為0?,F(xiàn)在,C庫已提供了用于檢查計(jì)算是否會產(chǎn)生低于正常值的函數(shù)。

還有另一個特殊的浮點(diǎn)值NaN(not a number的縮寫)。例如,給asin()函數(shù)傳遞一個值,該函數(shù)將返回一個角度,該角度的正弦就是傳入函數(shù)的值。但是正弦值不能大于1,因此,如果傳入的參數(shù)大于1,該函數(shù)的行為是未定義的。在這種情況下,該函數(shù)將返回NaN值,printf()函數(shù)可將其顯示為nan、NaN或其他類似的內(nèi)容。

浮點(diǎn)數(shù)舍入錯誤

給定一個數(shù),加上1,再減去原來給定的數(shù),結(jié)果是多少?你一定認(rèn)為是1。但是,下面的浮點(diǎn)運(yùn)算給出了不同的答案:

/* floaterr.c--演示舍入錯誤 */

#include <stdio.h>

int main(void)

{

? ? float a,b;

? ? b = 2.0e20 + 1.0;

? ? a = b - 2.0e20;

? ? printf("%f \n", a);

? ? return 0;

}

該程序的輸出如下:

得出這些奇怪答案的原因是,計(jì)算機(jī)缺少足夠的小數(shù)位來完成正確的運(yùn)算。2.0e20是 2后面有20個0。如果把該數(shù)加1,那么發(fā)生變化的是第21位。要正確運(yùn)算,程序至少要儲存21位數(shù)字。而float類型的數(shù)字通常只能儲存按指數(shù)比例縮小或放大的6或7位有效數(shù)字。在這種情況下,計(jì)算結(jié)果一定是錯誤的。另一方面,如果把2.0e20改成2.0e4,計(jì)算結(jié)果就沒問題。因?yàn)?.0e4加1只需改變第5位上的數(shù)字,float類型的精度足夠進(jìn)行這樣的計(jì)算。

浮點(diǎn)數(shù)表示法

上一個方框中列出了由于計(jì)算機(jī)使用的系統(tǒng)不同,一個程序有不同的輸出。原因是,根據(jù)前面介紹的知識,實(shí)現(xiàn)浮點(diǎn)數(shù)表示法的方法有多種。為了盡可能地統(tǒng)一實(shí)現(xiàn),電子和電氣工程師協(xié)會(IEEE)為浮點(diǎn)數(shù)計(jì)算和表示法開發(fā)了一套標(biāo)準(zhǔn)。現(xiàn)在,許多硬件浮點(diǎn)單元都采用該標(biāo)準(zhǔn)。2011年,該標(biāo)準(zhǔn)被ISO/IEC/IEEE 60559:2011標(biāo)準(zhǔn)收錄。該標(biāo)準(zhǔn)作為C99和C11的可選項(xiàng),符合硬件要求的平臺可開啟。floaterr.c程序的第3個輸出示例即是支持該浮點(diǎn)標(biāo)準(zhǔn)的系統(tǒng)顯示的結(jié)果。支持C標(biāo)準(zhǔn)的編譯器還包含捕獲異常問題的工具。詳見附錄B.5,參考資料V。

3.4.7 復(fù)數(shù)和虛數(shù)類型

許多科學(xué)和工程計(jì)算都要用到復(fù)數(shù)和虛數(shù)。C99 標(biāo)準(zhǔn)支持復(fù)數(shù)類型和虛數(shù)類型,但是有所保留。一些獨(dú)立實(shí)現(xiàn),如嵌入式處理器的實(shí)現(xiàn),就不需要使用復(fù)數(shù)和虛數(shù)(VCR芯片就不需要復(fù)數(shù))。一般而言,虛數(shù)類型都是可選項(xiàng)。C11標(biāo)準(zhǔn)把整個復(fù)數(shù)軟件包都作為可選項(xiàng)。

簡而言之,C語言有3種復(fù)數(shù)類型:float_Complex、double_Complex和long double_Complex。例如,float _Complex類型的變量應(yīng)包含兩個float類型的值,分別表示復(fù)數(shù)的實(shí)部和虛部。類似地, C語言的3種虛數(shù)類型是float_Imaginary、double _Imaginary和long double _Imaginary。

如果包含complex.h頭文件,便可用complex代替_Complex,用imaginary代替_Imaginary,還可以用I代替-1的平方根。

為何 C 標(biāo)準(zhǔn)不直接用 complex 作為關(guān)鍵字來代替_Complex,而要添加一個頭文件(該頭文件中把complex定義為_Complex)?因?yàn)闃?biāo)準(zhǔn)委員會考慮到,如果使用新的關(guān)鍵字,會導(dǎo)致以該關(guān)鍵字作為標(biāo)識符的現(xiàn)有代碼全部失效。例如,之前的 C99,許多程序員已經(jīng)使用 struct complex 定義一個結(jié)構(gòu)來表示復(fù)數(shù)或者心理學(xué)程序中的心理狀況(關(guān)鍵字struct用于定義能儲存多個值的結(jié)構(gòu),詳見第14章)。讓complex成為關(guān)鍵字會導(dǎo)致之前的這些代碼出現(xiàn)語法錯誤。但是,使用struct _Complex的人很少,特別是標(biāo)準(zhǔn)使用首字母是下劃線的標(biāo)識符作為預(yù)留字以后。因此,標(biāo)準(zhǔn)委員會選定_Complex作為關(guān)鍵字,在不用考慮名稱沖突的情況下可選擇使用complex。

3.4.8 其他類型

現(xiàn)在已經(jīng)介紹完C語言的所有基本數(shù)據(jù)類型。有些人認(rèn)為這些類型實(shí)在太多了,但有些人覺得還不夠用。注意,雖然C語言沒有字符串類型,但也能很好地處理字符串。第4章將詳細(xì)介紹相關(guān)內(nèi)容。

C語言還有一些從基本類型衍生的其他類型,包括數(shù)組、指針、結(jié)構(gòu)和聯(lián)合。盡管后面章節(jié)中會詳細(xì)介紹這些類型,但是本章的程序示例中已經(jīng)用到了指針〔指針(pointer)指向變量或其他數(shù)據(jù)對象位置〕。例如,在scanf()函數(shù)中用到的前綴&,便創(chuàng)建了一個指針,告訴 scanf()把數(shù)據(jù)放在何處。

小結(jié):基本數(shù)據(jù)類型

關(guān)鍵字:

基本數(shù)據(jù)類型由11個關(guān)鍵字組成:int、long、short、unsigned、char、float、double、signed、_Bool、_Complex和_Imaginary。

有符號整型:

有符號整型可用于表示正整數(shù)和負(fù)整數(shù)。

?int ——系統(tǒng)給定的基本整數(shù)類型。C語言規(guī)定int類型不小于16位。

?short或short int ——最大的short類型整數(shù)小于或等于最大的int類型整數(shù)。C語言規(guī)定short類型至少占16位。

?long或long int ——該類型可表示的整數(shù)大于或等于最大的int類型整數(shù)。C語言規(guī)定long類型至少占32位。

?long long或long long int ——該類型可表示的整數(shù)大于或等于最大的long類型整數(shù)。Long long類型至少占64位。

一般而言,long類型占用的內(nèi)存比short類型大,int類型的寬度要么和long類型相同,要么和short類型相同。例如,舊DOS系統(tǒng)的PC提供16位的short和int,以及32位的long;Windows 95系統(tǒng)提供16位的short以及32位的int和long。

無符號整型:

無符號整型只能用于表示零和正整數(shù),因此無符號整型可表示的正整數(shù)比有符號整型的大。在整型類型前加上關(guān)鍵字unsigned表明該類型是無符號整型:unsignedint、unsigned long、unsigned short。單獨(dú)的unsigned相當(dāng)于unsignedint。

字符類型:

可打印出來的符號(如A、&和+)都是字符。根據(jù)定義,char類型表示一個字符要占用1字節(jié)內(nèi)存。出于歷史原因,1字節(jié)通常是8位,但是如果要表示基本字符集,也可以是16位或更大。

?char ——字符類型的關(guān)鍵字。有些編譯器使用有符號的char,而有些則使用無符號的char。在需要時(shí),可在char前面加上關(guān)鍵字signed或unsigned來指明具體使用哪一種類型。

布爾類型:

布爾值表示true和false。C語言用1表示true,0表示false。

?_Bool ——布爾類型的關(guān)鍵字。布爾類型是無符號 int類型,所占用的空間只要能儲存0或1即可。

實(shí)浮點(diǎn)類型:

實(shí)浮點(diǎn)類型可表示正浮點(diǎn)數(shù)和負(fù)浮點(diǎn)數(shù)。

?float ——系統(tǒng)的基本浮點(diǎn)類型,可精確表示至少6位有效數(shù)字。

?double ——儲存浮點(diǎn)數(shù)的范圍(可能)更大,能表示比 float 類型更多的有效數(shù)字(至少 10位,通常會更多)和更大的指數(shù)。

?long double ——儲存浮點(diǎn)數(shù)的范圍(可能)比double更大,能表示比double更多的有效數(shù)字和更大的指數(shù)。

復(fù)數(shù)和虛數(shù)浮點(diǎn)數(shù):

虛數(shù)類型是可選的類型。復(fù)數(shù)的實(shí)部和虛部類型都基于實(shí)浮點(diǎn)類型來構(gòu)成:

?float _Complex

?double _Complex

?long double _Complex

?float _Imaginary

?double _Imaginary

?long long _Imaginary

小結(jié):如何聲明簡單變量

1.選擇需要的類型。

2.使用有效的字符給變量起一個變量名。

3.按以下格式進(jìn)行聲明:

類型說明符 變量名;

類型說明符由一個或多個關(guān)鍵字組成。下面是一些示例:

int erest;

unsigned short cash;

4.可以同時(shí)聲明相同類型的多個變量,用逗號分隔各變量名,如下所示:

char ch, init, ans;

5.在聲明的同時(shí)還可以初始化變量:

float mass = 6.0E24;

3.4.9 類型大小

如何知道當(dāng)前系統(tǒng)的指定類型的大小是多少?運(yùn)行程序清單3.8,會列出當(dāng)前系統(tǒng)的各類型的大小。

程序清單3.8 typesize.c程序

//* typesize.c -- 打印類型大小 */

#include <stdio.h>

int main(void)

?

{

? ? /* C99為類型大小提供%zd轉(zhuǎn)換說明 */

? ? printf("Type int has a size of %zd bytes.\n", sizeof(int));

? ? printf("Type char has a size of %zd bytes.\n", sizeof(char));

? ? printf("Type long has a size of %zd bytes.\n", sizeof(long));

? ? printf("Type long long has a size of %zd bytes.\n",

? ? ? ? sizeof(long long));

? ? printf("Type double has a size of %zd bytes.\n",

? ? ? ? sizeof(double));

? ? printf("Type long double has a size of %zd bytes.\n",

? ? ? ? sizeof(long double));

? ? return 0;

}

sizeof是C語言的內(nèi)置運(yùn)算符,以字節(jié)為單位給出指定類型的大小。C99和C11提供%zd轉(zhuǎn)換說明匹配sizeof的返回類型[2]。一些不支持C99和C11的編譯器可用%u或%lu代替%zd。

該程序的輸出如下:

Type int has a size of 4 bytes.

Type char has a size of 1 bytes.

Type long has a size of 8 bytes.

?

Type long long has a size of 8 bytes.

Type double has a size of 8 bytes.

Type long double has a size of 16 bytes.

該程序列出了6種類型的大小,你也可以把程序中的類型更換成感興趣的其他類型。注意,因?yàn)镃語言定義了char類型是1字節(jié),所以char類型的大小一定是1字節(jié)。而在char類型為16位、double類型為64位的系統(tǒng)中,sizeof給出的double是4字節(jié)。在limits.h和float.h頭文件中有類型限制的相關(guān)信息(下一章將詳細(xì)介紹這兩個頭文件)。

順帶一提,注意該程序最后幾行 printf()語句都被分為兩行,只要不在引號內(nèi)部或一個單詞中間斷行,就可以這樣寫。

3.5 使用數(shù)據(jù)類型

編寫程序時(shí),應(yīng)注意合理選擇所需的變量及其類型。通常,用int或float類型表示數(shù)字,char類型表示字符。在使用變量之前必須先聲明,并選擇有意義的變量名。初始化變量應(yīng)使用與變量類型匹配的常數(shù)類型。例如:

int apples = 3; /* 正確 */

int oranges = 3.0; /* 不好的形式 */

與Pascal相比,C在檢查類型匹配方面不太嚴(yán)格。C編譯器甚至允許二次初始化,但在激活了較高級別警告時(shí),會給出警告。最好不要養(yǎng)成這樣的習(xí)慣。

把一個類型的數(shù)值初始化給不同類型的變量時(shí),編譯器會把值轉(zhuǎn)換成與變量匹配的類型,這將導(dǎo)致部分?jǐn)?shù)據(jù)丟失。例如,下面的初始化:

int cost = 12.99; /* 用double類型的值初始化int類型的變量 */

float pi = 3.1415926536; /* 用double類型的值初始化float類型的變量 */

第1個聲明,cost的值是12。C編譯器把浮點(diǎn)數(shù)轉(zhuǎn)換成整數(shù)時(shí),會直接丟棄(截?cái)啵┬?shù)部分,而不進(jìn)行四舍五入。第2個聲明會損失一些精度,因?yàn)镃只保證了float類型前6位的精度。編譯器對這樣的初始化可能給出警告。讀者在編譯程序清單3.1時(shí)可能就遇到了這種警告。

許多程序員和公司內(nèi)部都有系統(tǒng)化的命名約定,在變量名中體現(xiàn)其類型。例如,用 i_前綴表示 int類型,us_前綴表示 unsigned short 類型。這樣,一眼就能看出來 i_smart 是 int 類型的變量, us_versmart是unsigned short類型的變量。

3.6 參數(shù)和陷阱

有必要再次提醒讀者注意 printf()函數(shù)的用法。讀者應(yīng)該還記得,傳遞給函數(shù)的信息被稱為參數(shù)。例如,printf("Hello, pal.")函數(shù)調(diào)用有一個參數(shù):"Hello,pal."。雙引號中的字符序列(如,"Hello,pal.")被稱為字符串(string),第4章將詳細(xì)講解相關(guān)內(nèi)容?,F(xiàn)在,關(guān)鍵是要理解無論雙引號中包含多少個字符和標(biāo)點(diǎn)符號,一個字符串就是一個參數(shù)。

與此類似,scanf("%d", &weight)函數(shù)調(diào)用有兩個參數(shù):"%d"和&weight。C語言用逗號分隔函數(shù)中的參數(shù)。printf()和scanf()函數(shù)與一般函數(shù)不同,它們的參數(shù)個數(shù)是可變的。例如,前面的程序示例中調(diào)用過帶一個、兩個,甚至三個參數(shù)的 printf()函數(shù)。程序要知道函數(shù)的參數(shù)個數(shù)才能正常工作。printf()和scanf()函數(shù)用第1個參數(shù)表明后續(xù)有多少個參數(shù),即第1個字符串中的轉(zhuǎn)換說明與后面的參數(shù)一一對應(yīng)。例如,下面的語句有兩個%d轉(zhuǎn)換說明,說明后面還有兩個參數(shù):

printf("%d cats ate %d cans of tuna\n", cats, cans);

后面的確還有兩個參數(shù):cats和cans。

程序員要負(fù)責(zé)確保轉(zhuǎn)換說明的數(shù)量、類型與后面參數(shù)的數(shù)量、類型相匹配。現(xiàn)在,C 語言通過函數(shù)原型機(jī)制檢查函數(shù)調(diào)用時(shí)參數(shù)的個數(shù)和類型是否正確。但是,該機(jī)制對printf()和scanf()不起作用,因?yàn)檫@兩個函數(shù)的參數(shù)個數(shù)可變。如果參數(shù)在匹配上有問題,會出現(xiàn)什么情況?假設(shè)你編寫了程序清單 3.9中的程序。

程序清單3.9 badcount.c程序

/* badcount.c -- 參數(shù)錯誤的情況 */

#include <stdio.h>

int main(void)

{

? ? int n = 4;

? ? int m = 5;

? ? float f = 7.0f;

? ? float g = 8.0f;

? ? printf("%d\n", n, m); /* 參數(shù)太多 */

? ? printf("%d %d %d\n", n); /* 參數(shù)太少 */

? ? printf("%d %d\n", f, g); /* 值的類型不匹配 */

? ? return 0;

}

XCode 4.6(OS 10.8)的輸出如下:

4

4 1 -706337836

1606414344 1

Microsoft Visual Studio Express 2012(Windows 7)的輸出如下:

4

4 0 0

0 1075576832

注意,用%d顯示float類型的值,其值不會被轉(zhuǎn)換成int類型。在不同的平臺下,缺少參數(shù)或參數(shù)類型不匹配導(dǎo)致的結(jié)果不同。

所有編譯器都能順利編譯并運(yùn)行該程序,但其中大部分會給出警告。的確,有些編譯器會捕獲到這類問題,然而C標(biāo)準(zhǔn)對此未作要求。因此,計(jì)算機(jī)在運(yùn)行時(shí)可能不會捕獲這類錯誤。如果程序正常運(yùn)行,很難覺察出來。如果程序沒有打印出期望值或打印出意想不到的值,你才會檢查 printf()函數(shù)中的參數(shù)個數(shù)和類型是否得當(dāng)。

3.7 轉(zhuǎn)義序列示例

再來看一個程序示例,該程序使用了一些特殊的轉(zhuǎn)義序列。程序清單3.10 演示了退格(\b)、水平制表符(\t)和回車(\t)的工作方式。這些概念在計(jì)算機(jī)使用電傳打字機(jī)作為輸出設(shè)備時(shí)就有了,但是它們不一定能與現(xiàn)代的圖形接口兼容。例如,程序清單3.10在某些Macintosh的實(shí)現(xiàn)中就無法正常運(yùn)行。

程序清單3.10 escape.c程序

/* escape.c -- 使用轉(zhuǎn)移序列 */

#include <stdio.h>

int main(void)

{

? ? float salary;

? ? printf("\aEnter your desired monthly salary:"); /* 1 */

? ? printf(" $_______\b\b\b\b\b\b\b"); /* 2 */

? ? scanf("%f", &salary);

? ? printf("\n\t$%.2f a month is $%.2f a year.", salary,

? ? ? ? salary * 12.0); /* 3 */

? ? printf("\rGee!\n"); /* 4 */

? ? return 0;

}

3.7.1 程序運(yùn)行情況

假設(shè)在系統(tǒng)中運(yùn)行的轉(zhuǎn)義序列行為與本章描述的行為一致(實(shí)際行為可能不同。例如,XCode 4.6把\a、\b和\r顯示為顛倒的問號),下面我們來分析這個程序。

第1條printf()語句(注釋中標(biāo)為1)發(fā)出一聲警報(bào)(因?yàn)槭褂昧薥a),然后打印下面的內(nèi)容:

Enter your desired monthly salary:

因?yàn)閜rintf()中的字符串末尾沒有\(zhòng)n,所以光標(biāo)停留在冒號后面。

第2條printf()語句在光標(biāo)處接著打印,屏幕上顯示的內(nèi)容是:

Enter your desired monthly salary: $_______

冒號和美元符號之間有一個空格,這是因?yàn)榈?條printf()語句中的字符串以一個空格開始。7個退格字符使得光標(biāo)左移7個位置,即把光標(biāo)移至7個下劃線字符的前面,緊跟在美元符號后面。通常,退格不會擦除退回所經(jīng)過的字符,但有些實(shí)現(xiàn)是擦除的,這和本例不同。

假設(shè)鍵入的數(shù)據(jù)是4000.00(并按下Enter鍵),屏幕顯示的內(nèi)容應(yīng)該是:

Enter your desired monthly salary: $4000.00

鍵入的字符替換了下劃線字符。按下Enter鍵后,光標(biāo)移至下一行的起始處。

第3條printf()語句中的字符串以\n\t開始。換行字符使光標(biāo)移至下一行起始處。水平制表符使光標(biāo)移至該行的下一個制表點(diǎn),一般是第9列(但不一定)。然后打印字符串中的其他內(nèi)容。執(zhí)行完該語句后,此時(shí)屏幕顯示的內(nèi)容應(yīng)該是:

Enter your desired monthly salary: $4000.00

? ? ? ? $4000.00 a month is $48000.00 a year.

因?yàn)檫@條printf()語句中沒有使用換行字符,所以光標(biāo)停留在最后的點(diǎn)號

后面。

第4條printf()語句以\r開始。這使得光標(biāo)回到當(dāng)前行的起始處。然后打印Gee!,接著\n使光標(biāo)移至下一行的起始處。屏幕最后顯示的內(nèi)容應(yīng)該是:

Enter your desired monthly salary: $4000.00

? ? ? ? Gee! $4000.00 a month is $48000.00 a year.

3.7.2 刷新輸出

printf()何時(shí)把輸出發(fā)送到屏幕上?最初,printf()語句把輸出發(fā)送到一個叫作緩沖區(qū)(buffer)的中間存儲區(qū)域,然后緩沖區(qū)中的內(nèi)容再不斷被發(fā)送到屏幕上。C 標(biāo)準(zhǔn)明確規(guī)定了何時(shí)把緩沖區(qū)中的內(nèi)容發(fā)送到屏幕:當(dāng)緩沖區(qū)滿、遇到換行字符或需要輸入的時(shí)候(從緩沖區(qū)把數(shù)據(jù)發(fā)送到屏幕或文件被稱為刷新緩沖區(qū))。例如,前兩個 printf()語句既沒有填滿緩沖區(qū),也沒有換行符,但是下一條 scanf()語句要求用戶輸入,這迫使printf()的輸出被發(fā)送到屏幕上。

舊式編譯器遇到scanf()也不會強(qiáng)行刷新緩沖區(qū),程序會停在那里不顯示任何提示內(nèi)容,等待用戶輸入數(shù)據(jù)。在這種情況下,可以使用換行字符刷新緩沖區(qū)。代碼應(yīng)改為:

printf("Enter your desired monthly salary:\n");

scanf("%f", &salary);

無論接下來的輸入是否能刷新緩沖區(qū),代碼都會正常運(yùn)行。這將導(dǎo)致光標(biāo)移至下一行起始處,用戶無法在提示內(nèi)容同一行輸入數(shù)據(jù)。還有一種刷新緩沖區(qū)的方法是使用fflush()函數(shù),詳見第13章。

3.8 關(guān)鍵概念

C語言提供了大量的數(shù)值類型,目的是為程序員提供方便。那以整數(shù)類型為例,C認(rèn)為一種整型不夠,提供了有符號、無符號,以及大小不同的整型,以滿足不同程序的需求。

計(jì)算機(jī)中的浮點(diǎn)數(shù)和整數(shù)在本質(zhì)上不同,其存儲方式和運(yùn)算過程有很大區(qū)別。即使兩個32位存儲單元儲存的位組合完全相同,但是一個解釋為float類型,另一個解釋為long類型,這兩個相同的位組合表示的值也完全不同。例如,在PC中,假設(shè)一個位組合表示float類型的數(shù)256.0,如果將其解釋為long類型,得到的值是113246208。C語言允許編寫混合數(shù)據(jù)類型的表達(dá)式,但是會進(jìn)行自動類型轉(zhuǎn)換,以便在實(shí)際運(yùn)算時(shí)統(tǒng)一使用一種類型。

計(jì)算機(jī)在內(nèi)存中用數(shù)值編碼來表示字符。美國最常用的是ASCII碼,除此之外C也支持其他編碼。字符常量是計(jì)算機(jī)系統(tǒng)使用的數(shù)值編碼的符號表示,它表示為單引號括起來的字符,如'A'。

3.9 本章小結(jié)

C有多種的數(shù)據(jù)類型?;緮?shù)據(jù)類型分為兩大類:整數(shù)類型和浮點(diǎn)數(shù)類型。通過為類型分配的儲存量以及是有符號還是無符號,區(qū)分不同的整數(shù)類型。最小的整數(shù)類型是char,因?qū)崿F(xiàn)不同,可以是有符號的char或無符號的char,即unsigned char或signed char。但是,通常用char類型表示小整數(shù)時(shí)才這樣顯示說明。其他整數(shù)類型有short、int、long和long long類型。C規(guī)定,后面的類型不能小于前面的類型。上述都是有符號類型,但也可以使用unsigned關(guān)鍵字創(chuàng)建相應(yīng)的無符號類型:unsigned short、unsigned int、unsigned long和unsigned long long?;蛘撸陬愋兔凹由蟬igned修飾符顯式表明該類型是有符號類型。最后,_Bool類型是一種無符號類型,可儲存0或1,分別代表false和true。

浮點(diǎn)類型有3種:float、double和C90新增的long double。后面的類型應(yīng)大于或等于前面的類型。有些實(shí)現(xiàn)可選擇支持復(fù)數(shù)類型和虛數(shù)類型,通過關(guān)鍵字_Complex和_Imaginary與浮點(diǎn)類型的關(guān)鍵字組合(如,double _Complex類型和float _Imaginary類型)來表示這些類型。

整數(shù)可以表示為十進(jìn)制、八進(jìn)制或十六進(jìn)制。0前綴表示八進(jìn)制數(shù),0x或0X前綴表示十六進(jìn)制數(shù)。例如,32、040、0x20分別以十進(jìn)制、八進(jìn)制、十六進(jìn)制表示同一個值。l或L前綴表明該值是long類型, ll或LL前綴表明該值是long long類型。

在C語言中,直接表示一個字符常量的方法是:把該字符用單引號括起來,如'Q'、'8'和'$'。C語言的轉(zhuǎn)義序列(如,'\n')表示某些非打印字符。另外,還可以在八進(jìn)制或十六進(jìn)制數(shù)前加上一個反斜杠(如,'\007'),表示ASCII碼中的一個字符。

浮點(diǎn)數(shù)可寫成固定小數(shù)點(diǎn)的形式(如,9393.912)或指數(shù)形式(如,7.38E10)。C99和C11提供了第3種指數(shù)表示法,即用十六進(jìn)制數(shù)和2的冪來表示(如,0xa.1fp10)。

printf()函數(shù)根據(jù)轉(zhuǎn)換說明打印各種類型的值。轉(zhuǎn)換說明最簡單的形式由一個百分號(%)和一個轉(zhuǎn)換字符組成,如%d或%f。

3.10 復(fù)習(xí)題

復(fù)習(xí)題的參考答案在附錄A中。

1.指出下面各種數(shù)據(jù)使用的合適數(shù)據(jù)類型(有些可使用多種數(shù)據(jù)類

型):

? ? a.East Simpleton的人口

? ? b.DVD影碟的價(jià)格

? ? c.本章出現(xiàn)次數(shù)最多的字母

? ? d.本章出現(xiàn)次數(shù)最多的字母次數(shù)

2.在什么情況下要用long類型的變量代替int類型的變量?

3.使用哪些可移植的數(shù)據(jù)類型可以獲得32位有符號整數(shù)?選擇的理由是

什么?

4.指出下列常量的類型和含義(如果有的話):

? ? a.'\b'

? ? b.1066

? ? c.99.44

? ? d.0XAA

? ? e.2.0e30

5.Dottie Cawm編寫了一個程序,請找出程序中的錯誤。

? ? include <stdio.h>?

? ? main

? ? (

? ? float g; h;

? ? float tax, rate;

? ? g = e21;

? ? tax = rate*g;

????)

6.寫出下列常量在聲明中使用的數(shù)據(jù)類型和在printf()中對應(yīng)的轉(zhuǎn)換說明:

7.寫出下列常量在聲明中使用的數(shù)據(jù)類型和在printf()中對應(yīng)的轉(zhuǎn)換說明(假設(shè)int為16位):

?

8.假設(shè)程序的開頭有下列聲明:

? ? int imate = 2;

? ? long shot = 53456;

? ? char grade = 'A';

? ? float log = 2.71828;

把下面printf()語句中的轉(zhuǎn)換字符補(bǔ)充完整:

? ? printf("The odds against the %__ were %__ to 1.\n",imate, shot);

? ? printf("A score of %__ is not an %__ grade.\n", log,grade);

9.假設(shè)ch是char類型的變量。分別使用轉(zhuǎn)義序列、十進(jìn)制值、八進(jìn)制字

符常量和十六進(jìn)制字符常量把回車字符賦給ch(假設(shè)使用ASCII編碼值)。

10.修正下面的程序(在C中,/表示除以)。

????void main(int) / this program is perfect /

????{

????????cows, legs integer;

????????printf("How many cow legs did you count?\n);

????????scanf("%c", legs);

????????cows = legs / 4;

????????printf("That implies there are %f cows.\n", cows)

????}

11.指出下列轉(zhuǎn)義序列的含義:

????a.\n

????b.\\

????c.\"

????d.\t

3.11 編程練習(xí)

1.通過試驗(yàn)(即編寫帶有此類問題的程序)觀察系統(tǒng)如何處理整數(shù)上溢、浮點(diǎn)數(shù)上溢和浮點(diǎn)數(shù)下溢的情況。

2.編寫一個程序,要求提示輸入一個ASCII碼值(如,66),然后打印輸入的字符。

3.編寫一個程序,發(fā)出一聲警報(bào),然后打印下面的文本:

????Startled by the sudden sound, Sally shouted,

????"By the Great Pumpkin, what was that!"

4.編寫一個程序,讀取一個浮點(diǎn)數(shù),先打印成小數(shù)點(diǎn)形式,再打印成指數(shù)形式。然后,如果系統(tǒng)支持,再打印成p記數(shù)法(即十六進(jìn)制記數(shù)法)。按以下格式輸出(實(shí)際顯示的指數(shù)位數(shù)因系統(tǒng)而異):

????Enter a floating-point value: 64.25

????fixed-point notation: 64.250000

????exponential notation: 6.425000e+01

????p notation: 0x1.01p+6

5.一年大約有3.156×10^7秒。編寫一個程序,提示用戶輸入年齡,然后顯示該年齡對應(yīng)的秒數(shù)。

6.1個水分子的質(zhì)量約為3.0×10^?23克。1夸脫水大約是950克。編寫一個程序,提示用戶輸入水的夸脫數(shù),并顯示水分子的數(shù)量。

7.1英寸相當(dāng)于2.54厘米。編寫一個程序,提示用戶輸入身高(/英寸),然后以厘米為單位顯示身高。

?8.在美國的體積測量系統(tǒng)中,1品脫等于2杯,1杯等于8盎司,1盎司等于2大湯勺,1大湯勺等于3茶勺。編寫一個程序,提示用戶輸入杯數(shù),并以品脫、盎司、湯勺、茶勺為單位顯示等價(jià)容量。思考對于該程序,為何使用浮點(diǎn)類型比整數(shù)類型更合適?

[1].歐美日常使用的度量衡單位是常衡盎司(avoirdupois ounce),而歐美黃

金市場上使用的黃金交易計(jì)量單位是金衡盎司(troy ounce)。國際黃金市

場上的報(bào)價(jià),其單位“盎司”都指的是黃金盎司。常衡盎司屬英制計(jì)量單位,

做重量單位時(shí)也稱為英兩。相關(guān)換算參考如下:1常衡盎司 = 28.350克,1金

衡盎司 = 31.104克,16常衡盎司 = 1磅。該程序的單位轉(zhuǎn)換思路是:把磅換

算成金衡盎司,即28.350÷31.104×16=14.5833?!g者注

[2].即,size_t類型。——譯者注


【轉(zhuǎn)載】C Primer Plus(第6版):第3章 數(shù)據(jù)和C的評論 (共 條)

分享到微博請遵守國家法律
攀枝花市| 霍林郭勒市| 南岸区| 九寨沟县| 锡林浩特市| 尉氏县| 新乡市| 阳信县| 龙里县| 资阳市| 云林县| 五峰| 天柱县| 连平县| 天长市| 崇阳县| 克东县| 浦城县| 宁乡县| 化德县| 堆龙德庆县| 旬邑县| 威海市| 张北县| 平果县| 邳州市| 延吉市| 三原县| 阿克| 沙洋县| 武强县| 依兰县| 庆云县| 深圳市| 龙海市| 阿拉善右旗| 临猗县| 息烽县| 浮山县| 习水县| 清流县|