五月天青色头像情侣网名,国产亚洲av片在线观看18女人,黑人巨茎大战俄罗斯美女,扒下她的小内裤打屁股

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

【轉(zhuǎn)載】C Primer Plus(第6版):第2章 C語(yǔ)言概述

2023-12-06 17:34 作者:安塞腰果樂(lè)  | 我要投稿

第2章 C語(yǔ)言概述

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

?運(yùn)算符:=

?函數(shù):main()、printf()

?編寫一個(gè)簡(jiǎn)單的C程序

?創(chuàng)建整型變量,為其賦值并在屏幕上顯示其值

?換行字符

?如何在程序中寫注釋,創(chuàng)建包含多個(gè)函數(shù)的程序,發(fā)現(xiàn)程序的錯(cuò)誤

?什么是關(guān)鍵字

C程序是什么樣子的?瀏覽本書,能看到許多示例。初見(jiàn) C 程序會(huì)覺(jué)得有些古怪,程序中有許多{、cp->tort和*ptr++這樣的符號(hào)。然而,在學(xué)習(xí)C的過(guò)程中,對(duì)這些符號(hào)和C語(yǔ)言特有的其他符號(hào)會(huì)越來(lái)越熟悉,甚至?xí)矚g上它們。如果熟悉與C相關(guān)的其他語(yǔ)言,會(huì)對(duì)C語(yǔ)言有似曾相識(shí)的感覺(jué)。本章,我們從演示一個(gè)簡(jiǎn)單的程序示例開(kāi)始,解釋該程序的功能。同時(shí),強(qiáng)調(diào)一些C語(yǔ)言的基本特性。

2.1 簡(jiǎn)單的C程序示例

我們來(lái)看一個(gè)簡(jiǎn)單的C程序,如程序清單2.1所示。該程序演示了用C語(yǔ)言編程的一些基本特性。請(qǐng)先通讀程序清單2.1,看看自己是否能明白該程序的用途,再認(rèn)真閱讀后面的解釋。

程序清單2.1 first.c程序

#include <stdio.h>

int main(void) /* 一個(gè)簡(jiǎn)單的C程序 */

{

????int num; /* 定義一個(gè)名為num的變量 */

????num = 1; /* 為num賦一個(gè)值 */

????printf("I am a simple "); /* 使用printf()函數(shù) */

????printf("computer.\n");

????printf("My favorite number is %d because it is first.\n",num);

????return 0;

}

如果你認(rèn)為該程序會(huì)在屏幕上打印一些內(nèi)容,那就對(duì)了!光看程序也許并不知道打印的具體內(nèi)容,所以,運(yùn)行該程序,并查看結(jié)果。首先,用你熟悉的編輯器(或者編譯器提供的編輯器)創(chuàng)建一個(gè)包含程序清單2.1 中所有內(nèi)容的文件。給該文件命名,并以.c作為擴(kuò)展名,以滿足當(dāng)前系統(tǒng)對(duì)文件名的要求。例如,可以使用first.c?,F(xiàn)在,編譯并運(yùn)行該程序(查看第1章,復(fù)習(xí)該步驟的具體內(nèi)容)。如果一切運(yùn)行正常,該程序的輸出應(yīng)該是:

I am a simple computer.

My favorite number is 1 because it is first.

總而言之,結(jié)果在意料之中,但是程序中的\n 和%d 是什么?程序中有幾行代碼看起來(lái)有點(diǎn)奇怪。接下來(lái),我們逐行解釋這個(gè)程序。

程序調(diào)整

程序的輸出是否在屏幕上一閃而過(guò)?某些窗口環(huán)境會(huì)在單獨(dú)的窗口運(yùn)行程序,然后在程序運(yùn)行結(jié)束后自動(dòng)關(guān)閉窗口。如果遇到這種情況,可以在程序中添加額外的代碼,讓窗口等待用戶按下一個(gè)鍵后才關(guān)閉。一種方法是,在程序的return語(yǔ)句前添加一行代碼:

getchar();

這行代碼會(huì)讓程序等待擊鍵,窗口會(huì)在用戶按下一個(gè)鍵后才關(guān)閉。在第8章中會(huì)詳細(xì)介紹 getchar()的內(nèi)容。

2.2 示例解釋

我們會(huì)把程序清單2.1的程序分析兩遍。第1遍(快速概要)概述程序中每行代碼的作用,幫助讀者初步了解程序。第2遍(程序細(xì)節(jié))詳細(xì)分析代碼的具體含義,幫助讀者深入理解程序。

圖2.1總結(jié)了組成C程序的幾個(gè)部分[1],圖中包含的元素比第1個(gè)程序多。

2.2.1 第1遍:快速概要

本節(jié)簡(jiǎn)述程序中的每行代碼的作用。下一節(jié)詳細(xì)討論代碼的含義。

#include<stdio.h> ←包含另一個(gè)文件

該行告訴編譯器把stdio.h中的內(nèi)容包含在當(dāng)前程序中。stdio.h是C編譯器軟件包的標(biāo)準(zhǔn)部分,它提供鍵盤輸入和屏幕輸出的支持。

int main(void) ←函數(shù)名

C程序包含一個(gè)或多個(gè)函數(shù),它們是C程序的基本模塊。程序清單2.1的程序中有一個(gè)名為main()的函數(shù)。圓括號(hào)表明main()是一個(gè)函數(shù)名。int表明main()函數(shù)返回一個(gè)整數(shù),void表明main()不帶任何參數(shù)。這些內(nèi)容我們稍后詳述?,F(xiàn)在,只需記住int和void是標(biāo)準(zhǔn)ANSI C定義main()的一部分(如果使用ANSI C之前的編譯器,請(qǐng)省略void;考慮到兼容的問(wèn)題,請(qǐng)盡量使用較新的C編譯器)。

/* 一個(gè)簡(jiǎn)單的C程序 */ ←注釋

注釋在/*和*/兩個(gè)符號(hào)之間,這些注釋能提高程序的可讀性。注意,注釋只是為了幫助讀者理解程序,編譯器會(huì)忽略它們。

{ ←函數(shù)體開(kāi)始

左花括號(hào)表示函數(shù)定義開(kāi)始,右花括號(hào)(})表示函數(shù)定義結(jié)束。

int num; ←聲明

該聲明表明,將使用一個(gè)名為num的變量,而且num是int(整數(shù))類型。

num = 1; ←賦值表達(dá)式語(yǔ)句

語(yǔ)句num = 1;把值1賦給名為num的變量。

printf("I am a simple "); ←調(diào)用一個(gè)函數(shù)

該語(yǔ)句使用 printf()函數(shù),在屏幕上顯示 I am a simple,光標(biāo)停在同一行。printf()是標(biāo)準(zhǔn)的C庫(kù)函數(shù)。在程序中使用函數(shù)叫作調(diào)用函數(shù)。

printf("computer.\n"); ←調(diào)用另一個(gè)函數(shù)

接下來(lái)調(diào)用的這個(gè)printf()函數(shù)在上條語(yǔ)句打印出來(lái)的內(nèi)容后面加上“computer”。代碼\n告訴計(jì)算機(jī)另起一行,即把光標(biāo)移至下一行。

printf("My favorite number is %d because it is first.\n", num);

最后調(diào)用的printf()把num的值(1)內(nèi)嵌在用雙引號(hào)括起來(lái)的內(nèi)容中一并打印。%d告訴計(jì)算機(jī)以何種形式輸出num的值,打印在何處。

return 0; ←return語(yǔ)句

C函數(shù)可以給調(diào)用方提供(或返回)一個(gè)數(shù)。目前,可暫時(shí)把該行看作是結(jié)束main()函數(shù)的要求。

} ←結(jié)束

必須以右花括號(hào)表示程序結(jié)束。

2.2.2 第2遍:程序細(xì)節(jié)

瀏覽完程序清單2.1后,我們來(lái)仔細(xì)分析這個(gè)程序。再次強(qiáng)調(diào),本節(jié)將逐行分析程序中的代碼,以每行代碼為出發(fā)點(diǎn),深入分析代碼背后的細(xì)節(jié),為更全面地學(xué)習(xí)C語(yǔ)言編程的特性夯實(shí)基礎(chǔ)。

1.#include指令和頭文件

#include<stdio.h>

這是程序的第1行。#include <stdio.h>的作用相當(dāng)于把stdio.h文件中的所有內(nèi)容都輸入該行所在的位置。實(shí)際上,這是一種“拷貝-粘貼”的操作。include 文件提供了一種方便的途徑共享許多程序共有的信息。

#include這行代碼是一條C預(yù)處理器指令(preprocessor directive)。通常,C編譯器在編譯前會(huì)對(duì)源代碼做一些準(zhǔn)備工作,即預(yù)處理(preprocessing)。

所有的C編譯器軟件包都提供stdio.h文件。該文件中包含了供編譯器使用的輸入和輸出函數(shù)(如, printf())信息。該文件名的含義是標(biāo)準(zhǔn)輸入/輸出頭文件。通常,在C程序頂部的信息集合被稱為頭文件(header)。

在大多數(shù)情況下,頭文件包含了編譯器創(chuàng)建最終可執(zhí)行程序要用到的信息。例如,頭文件中可以定義一些常量,或者指明函數(shù)名以及如何使用它們。但是,函數(shù)的實(shí)際代碼在一個(gè)預(yù)編譯代碼的庫(kù)文件中。簡(jiǎn)而言之,頭文件幫助編譯器把你的程序正確地組合在一起。

ANSI/ISO C規(guī)定了C編譯器必須提供哪些頭文件。有些程序要包含stdio.h,而有些不用。特定C實(shí)現(xiàn)的文檔中應(yīng)該包含對(duì)C庫(kù)函數(shù)的說(shuō)明。這些說(shuō)明確定了使用哪些函數(shù)需要包含哪些頭文件。例如,要使用printf()函數(shù),必須包含stdio.h頭文件。省略必要的頭文件可能不會(huì)影響某一特定程序,但是最好不要這樣做。本書每次用到庫(kù)函數(shù),都會(huì)用#include指令包含ANSI/ISO標(biāo)準(zhǔn)指定的頭文件。

注意 為何不內(nèi)置輸入和輸出

讀者一定很好奇,為何不把輸入和輸出這些基本功能內(nèi)置在語(yǔ)言中。原因之一是,并非所有的程序都會(huì)用到I/O(輸入/輸出)包。輕裝上陣表現(xiàn)了C語(yǔ)言的哲學(xué)。正是這種經(jīng)濟(jì)使用資源的原則,使得C語(yǔ)言成為流行的嵌入式編程語(yǔ)言(例如,編寫控制汽車自動(dòng)燃油系統(tǒng)或藍(lán)光播放機(jī)芯片的代碼)。#include中的#符號(hào)表明,C預(yù)處理器在編譯器接手之前處理這條指令。本書后面章節(jié)中會(huì)介紹更多預(yù)處理器指令的示例,第16章將更詳細(xì)地討論相關(guān)內(nèi)容。

2.main()函數(shù)

int main(void);

程序清單2.1中的第2行表明該函數(shù)名為main。的確,main是一個(gè)極其普通的名稱,但是這是唯一的選擇。C程序一定從main()函數(shù)開(kāi)始執(zhí)行(目前不必考慮例外的情況)。除了main()函數(shù),你可以任意命名其他函數(shù),而且main()函數(shù)必須是開(kāi)始的函數(shù)。圓括號(hào)有什么功能?用于識(shí)別main()是一個(gè)函數(shù)。很快你將學(xué)到更多的函數(shù)。就目前而言,只需記住函數(shù)是C程序的基本模塊。

int是main()函數(shù)的返回類型。這表明main()函數(shù)返回的值是整數(shù)。返回到哪里?返回給操作系統(tǒng)。我們將在第6章中再來(lái)探討這個(gè)問(wèn)題。

通常,函數(shù)名后面的圓括號(hào)中包含一些傳入函數(shù)的信息。該例中沒(méi)有傳遞任何信息。因此,圓括號(hào)內(nèi)是單詞void(第11章將介紹把信息從main()函數(shù)傳回操作系統(tǒng)的另一種形式)。

如果瀏覽舊式的C代碼,會(huì)發(fā)現(xiàn)程序以如下形式開(kāi)始:

main()

C90標(biāo)準(zhǔn)勉強(qiáng)接受這種形式,但是C99和C11標(biāo)準(zhǔn)不允許這樣寫。因此,即使你使用的編譯器允許,也不要這樣寫。

你還會(huì)看到下面這種形式:

void main()

一些編譯器允許這樣寫,但是所有的標(biāo)準(zhǔn)都未認(rèn)可這種寫法。因此,編譯器不必接受這種形式,而且許多編譯器都不能這樣寫。需要強(qiáng)調(diào)的是,只要堅(jiān)持使用標(biāo)準(zhǔn)形式,把程序從一個(gè)編譯器移至另一個(gè)編譯器時(shí)就不會(huì)出什么問(wèn)題。

3.注釋

/*一個(gè)簡(jiǎn)單的程序*/

在程序中,被/* */兩個(gè)符號(hào)括起來(lái)的部分是程序的注釋。寫注釋能讓他人(包括自己)更容易明白你所寫的程序。C 語(yǔ)言注釋的好處之一是,可將注釋放在任意的地方,甚至是與要解釋的內(nèi)容在同一行。較長(zhǎng)的注釋可單獨(dú)放一行或多行。在/*和*/之間的內(nèi)容都會(huì)被編譯器忽略。下面列出了一些有效和無(wú)效的注釋形式:

/* 這是一條C注釋。 */

/* 這也是一條注釋,

? ? 被分成兩行。*/

/*

也可以這樣寫注釋。

*/

/* 這條注釋無(wú)效,因?yàn)槿鄙倭私Y(jié)束標(biāo)記。

C99新增了另一種風(fēng)格的注釋,普遍用于C++和Java。這種新風(fēng)格使用//符號(hào)創(chuàng)建注釋,僅限于單行。

// 這種注釋只能寫成一行。

int rigue; // 這種注釋也可置于此。

因?yàn)橐恍心┪簿蜆?biāo)志著注釋的結(jié)束,所以這種風(fēng)格的注釋只需在注釋開(kāi)始處標(biāo)明//符號(hào)即可。

這種新形式的注釋是為了解決舊形式注釋存在的潛在問(wèn)題。假設(shè)有下面的代碼:

/*

希望能運(yùn)行。

*/

x = 100;

y = 200;

/* 其他內(nèi)容已省略。 */

接下來(lái),假設(shè)你決定刪除第4行,但不小心刪掉了第3行(*/)。代碼如下所示:

/*

希望能運(yùn)行。

y = 200;

/*其他內(nèi)容已省略。 */

現(xiàn)在,編譯器把第1行的/*和第4行的*/配對(duì),導(dǎo)致4行代碼全都成了注釋(包括應(yīng)作為代碼的那一行)。而//形式的注釋只對(duì)單行有效,不會(huì)導(dǎo)致這種“消失代碼”的問(wèn)題。

一些編譯器可能不支持這一特性。還有一些編譯器需要更改設(shè)置,才能支持C99或C11的特性。

考慮到只用一種注釋風(fēng)格過(guò)于死板乏味,本書在示例中采用兩種風(fēng)格的注釋。

4.花括號(hào)、函數(shù)體和塊

{

...

}

程序清單2.1中,花括號(hào)把main()函數(shù)括起來(lái)。一般而言,所有的C函數(shù)都使用花括號(hào)標(biāo)記函數(shù)體的開(kāi)始和結(jié)束。這是規(guī)定,不能省略。只有花括號(hào)({})能起這種作用,圓括號(hào)(())和方括號(hào)([])都不行。

花括號(hào)還可用于把函數(shù)中的多條語(yǔ)句合并為一個(gè)單元或塊。如果讀者熟悉Pascal、ADA、Modula-2或者Algol,就會(huì)明白花括號(hào)在C語(yǔ)言中的作用類似于這些語(yǔ)言中的begin和end。

5.聲明

int num;

程序清單2.1中,這行代碼叫作聲明(declaration)。聲明是C語(yǔ)言最重要的特性之一。在該例中,聲明完成了兩件事。其一,在函數(shù)中有一個(gè)名為num的變量(variable)。其二,int表明num是一個(gè)整數(shù)(即,沒(méi)有小數(shù)點(diǎn)或小數(shù)部分的數(shù))。int是一種數(shù)據(jù)類型。編譯器使用這些信息為num變量在內(nèi)存中分配存儲(chǔ)空間。分號(hào)在C語(yǔ)言中是大部分語(yǔ)句和聲明的一部分,不像在Pascal中只是語(yǔ)句間的分隔符。

int是C語(yǔ)言的一個(gè)關(guān)鍵字(keyword),表示一種基本的C語(yǔ)言數(shù)據(jù)類型。關(guān)鍵字是語(yǔ)言定義的單詞,不能做其他用途。例如,不能用int作為函數(shù)名和變量名。但是,這些關(guān)鍵字在該語(yǔ)言以外不起作用,所以把一只貓或一個(gè)可愛(ài)的小孩叫int是可以的(盡管某些地方的當(dāng)?shù)亓?xí)俗或法律可能不允許)。

示例中的num是一個(gè)標(biāo)識(shí)符(identifier),也就一個(gè)變量、函數(shù)或其他實(shí)體的名稱。因此,聲明把特定標(biāo)識(shí)符與計(jì)算機(jī)內(nèi)存中的特定位置聯(lián)系起來(lái),同時(shí)也確定了儲(chǔ)存在某位置的信息類型或數(shù)據(jù)類型。

在C語(yǔ)言中,所有變量都必須先聲明才能使用。這意味著必須列出程序中用到的所有變量名及其類型。以前的C語(yǔ)言,還要求把變量聲明在塊的頂部,其他語(yǔ)句不能在任何聲明的前面。也就是說(shuō),main()函數(shù)體如下所示:

int main() //舊規(guī)則

{

? ? int doors;

? ? int dogs;

? ? doors = 5;

? ? dogs = 3;

? ? // 其他語(yǔ)句

}

C99和C11遵循C++的慣例,可以把聲明放在塊中的任何位置。盡管如

此,首次使用變量之前一定要先聲明它。因此,如果編譯器支持這一新特

性,可以這樣編寫上面的代碼:

int main() // 目前的C規(guī)則

{

? ? // 一些語(yǔ)句

? ? int doors;

? ? doors = 5; // 第1次使用doors

? ? // 其他語(yǔ)句

? ? int dogs;

? ? dogs = 3; // 第1次使用dogs

? ? // 其他語(yǔ)句

}

為了與舊系統(tǒng)更好地兼容,本書沿用最初的規(guī)則(即,把變量聲明都寫在塊的頂部)。

現(xiàn)在,讀者可能有3個(gè)問(wèn)題:什么是數(shù)據(jù)類型?如何命名?為何要聲明變量?請(qǐng)往下看。

數(shù)據(jù)類型

C語(yǔ)言可以處理多種類型的數(shù)據(jù),如整數(shù)、字符和浮點(diǎn)數(shù)。把變量聲明為整型或字符類型,計(jì)算機(jī)才能正確地儲(chǔ)存、讀取和解釋數(shù)據(jù)。下一章將詳細(xì)介紹C語(yǔ)言中的各種數(shù)據(jù)類型。

命名

給變量命名時(shí)要使用有意義的變量名或標(biāo)識(shí)符(如,程序中需要一個(gè)變量數(shù)羊,該變量名應(yīng)該是sheep_count而不是x3)。如果變量名無(wú)法清楚地表達(dá)自身的用途,可在注釋中進(jìn)一步說(shuō)明。這是一種良好的編程習(xí)慣和編程技巧。

C99和C11允許使用更長(zhǎng)的標(biāo)識(shí)符名,但是編譯器只識(shí)別前63個(gè)字符。對(duì)于外部標(biāo)識(shí)符(參閱第12章),只允許使用31個(gè)字符?!惨郧癈90只允許6個(gè)字符,這是一個(gè)很大的進(jìn)步。舊式編譯器通常最多只允許使用8個(gè)字符?!硨?shí)際上,你可以使用更長(zhǎng)的字符,但是編譯器會(huì)忽略超出的字符。也就是說(shuō),如果有兩個(gè)標(biāo)識(shí)符名都有63個(gè)字符,只有一個(gè)字符不同,那么編譯器會(huì)識(shí)別這是兩個(gè)不同的名稱。如果兩個(gè)標(biāo)識(shí)符都是64個(gè)字符,只有最后一個(gè)字符不同,那么編譯器可能將其視為同一個(gè)名稱,也可能不會(huì)。標(biāo)準(zhǔn)并未定義在這種情況下會(huì)發(fā)生什么。

可以用小寫字母、大寫字母、數(shù)字和下劃線(_)來(lái)命名。而且,名稱的第1個(gè)字符必須是字符或下劃線,不能是數(shù)字。表2.1給出了一些示例。

操作系統(tǒng)和C庫(kù)經(jīng)常使用以一個(gè)或兩個(gè)下劃線字符開(kāi)始的標(biāo)識(shí)符(如,_kcab),因此最好避免在自己的程序中使用這種名稱。標(biāo)準(zhǔn)標(biāo)簽都以一個(gè)或兩個(gè)下劃線字符開(kāi)始,如庫(kù)標(biāo)識(shí)符。這樣的標(biāo)識(shí)符都是保留的。這意味著,雖然使用它們沒(méi)有語(yǔ)法錯(cuò)誤,但是會(huì)導(dǎo)致名稱沖突。

C語(yǔ)言的名稱區(qū)分大小寫,即把一個(gè)字母的大寫和小寫視為兩個(gè)不同的字符。因此,stars和Stars、STARS都不同。

為了讓C語(yǔ)言更加國(guó)際化,C99和C11根據(jù)通用字符名(即UCN)機(jī)制添加了擴(kuò)展字符集。其中包含了除英文字母以外的部分字符。欲了解詳細(xì)內(nèi)容,請(qǐng)參閱附錄B的“參考資料VII:擴(kuò)展字符支持”。

聲明變量的4個(gè)理由

一些更老的語(yǔ)言(如,F(xiàn)ORTRAN 和 BASIC 的最初形式)都允許直接使用變量,不必先聲明。為何 C語(yǔ)言不采用這種簡(jiǎn)單易行的方法?原因如下。

?把所有的變量放在一處,方便讀者查找和理解程序的用途。如果變量名都是有意義的(如,taxtate而不是 r),這樣做效果很好。如果變量名無(wú)法表述清楚,在注釋中解釋變量的含義。這種方法讓程序的可讀性更高。

?聲明變量會(huì)促使你在編寫程序之前做一些計(jì)劃。程序在開(kāi)始時(shí)要獲得哪些信息?希望程序如何輸出?表示數(shù)據(jù)最好的方式是什么?

?聲明變量有助于發(fā)現(xiàn)隱藏在程序中的小錯(cuò)誤,如變量名拼寫錯(cuò)誤。例如,假設(shè)在某些不需要聲明就可以直接使用變量的語(yǔ)言中,編寫如下語(yǔ)句:

RADIUS1 = 20.4;

在后面的程序中,誤寫成:

CIRCUM = 6.28 * RADIUSl;

你不小心把數(shù)字1打成小寫字母l。這些語(yǔ)言會(huì)創(chuàng)建一個(gè)新的變量RADIUSl,并使用該變量中的值(也許是0,也許是垃圾值),導(dǎo)致賦給CIRCUM的值是錯(cuò)誤值。你可能要花很久時(shí)間才能查出原因。這樣的錯(cuò)誤在C語(yǔ)言中不會(huì)發(fā)生(除非你很不明智地聲明了兩個(gè)極其相似的變量),因?yàn)榫幾g器在發(fā)現(xiàn)未聲明的RADIUSl時(shí)會(huì)報(bào)錯(cuò)。

?如果事先未聲明變量,C程序?qū)o(wú)法通過(guò)編譯。如果前幾個(gè)理由還不足以說(shuō)服你,這個(gè)理由總可以讓你認(rèn)真考慮一下了。

如果要聲明變量,應(yīng)該聲明在何處?前面提到過(guò),C99之前的標(biāo)準(zhǔn)要求把聲明都置于塊的頂部,這樣規(guī)定的好處是:把聲明放在一起更容易理解程序的用途。C99 允許在需要時(shí)才聲明變量,這樣做的好處是:在給變量賦值之前聲明變量,就不會(huì)忘記給變量賦值。但是實(shí)際上,許多編譯器都還不支持C99。

6.賦值

num = 1;

程序清單中的這行代碼是賦值表達(dá)式語(yǔ)句[2]。賦值是C語(yǔ)言的基本操作之一。該行代碼的意思是“把值1賦給變量num”。在執(zhí)行int num;聲明時(shí),編譯器在計(jì)算機(jī)內(nèi)存中為變量num預(yù)留了空間,然后在執(zhí)行這行賦值表達(dá)式語(yǔ)句時(shí),把值儲(chǔ)存在之前預(yù)留的位置??梢越onum賦不同的值,這就是num之所以被稱為變量(variable)的原因。注意,該賦值表達(dá)式語(yǔ)句從右側(cè)把值賦到左側(cè)。另外,該語(yǔ)句以分號(hào)結(jié)尾,如圖2.2所示。

7.printf()函數(shù)

printf("I am a simple ");

printf("computer.\n");

printf("My favorite number is %d because it is first.\n", num);

這3行都使用了C語(yǔ)言的一個(gè)標(biāo)準(zhǔn)函數(shù):printf()。圓括號(hào)表明printf是一個(gè)函數(shù)名。圓括號(hào)中的內(nèi)容是從main()函數(shù)傳遞給printf()函數(shù)的信息。例如,上面的第1行把I am a simple傳遞給printf()函數(shù)。該信息被稱為參數(shù),或者更確切地說(shuō),是函數(shù)的實(shí)際參數(shù)(actual argument),如圖2.3所示?!苍贑語(yǔ)言中,實(shí)際參數(shù)(簡(jiǎn)稱實(shí)參)是傳遞給函數(shù)的特定值,形式參數(shù)(簡(jiǎn)稱形參)是函數(shù)中用于儲(chǔ)存值的變量。第5章中將詳述相關(guān)內(nèi)容。〕printf()函數(shù)用參數(shù)來(lái)做什么?該函數(shù)會(huì)查看雙引號(hào)中的內(nèi)容,并將其打印在屏幕上。

第1行printf()演示了在C語(yǔ)言中如何調(diào)用函數(shù)。只需輸入函數(shù)名,把所需的參數(shù)填入圓括號(hào)即可。當(dāng)程序運(yùn)行到這一行時(shí),控制權(quán)被轉(zhuǎn)給已命名的函數(shù)(該例中是printf())。函數(shù)執(zhí)行結(jié)束后,控制權(quán)被返回至主調(diào)函數(shù)(calling function),該例中是main()。

第2行printf()函數(shù)的雙引號(hào)中的\n字符并未輸出。這是為什么?\n的意思是換行。\n組合(依次輸入這兩個(gè)字符)代表一個(gè)換行符(newlinecharacter)。對(duì)于printf()而言,它的意思是“在下一行的最左邊開(kāi)始新的一行”。也就是說(shuō),打印換行符的效果與在鍵盤按下Enter鍵相同。既然如此,為何不在鍵入printf()參數(shù)時(shí)直接使用Enter鍵?因?yàn)榫庉嬈骺赡苷J(rèn)為這是直接的命令,而不是儲(chǔ)存在在源代碼中的指令。換句話說(shuō),如果直接按下Enter鍵,編輯器會(huì)退出當(dāng)前行并開(kāi)始新的一行。但是,換行符僅會(huì)影響程序輸出的顯示格式。

換行符是一個(gè)轉(zhuǎn)義序列(escape sequence)。轉(zhuǎn)義序列用于代表難以表示或無(wú)法輸入的字符。如,\t代表Tab鍵,\b代表Backspace鍵(退格鍵)。每個(gè)轉(zhuǎn)義序列都以反斜杠字符(\)開(kāi)始。我們?cè)诘?章中再來(lái)探討相關(guān)內(nèi)容。

這樣,就解釋了為什么3行printf()語(yǔ)句只打印出兩行:第1個(gè)printf()打印的內(nèi)容中不含換行符,但是第2和第3個(gè)printf()中都有換行符。

第3個(gè)printf()還有一些不明之處:參數(shù)中的%d在打印時(shí)有什么作用?先來(lái)看該函數(shù)的輸出:

My favorite number is 1 because it is first.

對(duì)比發(fā)現(xiàn),參數(shù)中的%d被數(shù)字1代替了,而1就是變量num的值。%d相當(dāng)于是一個(gè)占位符,其作用是指明輸出num值的位置。該行和下面的BASIC語(yǔ)句很像:

PRINT "My favorite number is "; num; " because it is first."

實(shí)際上,C語(yǔ)言的printf()比BASIC的這條語(yǔ)句做的事情多一些。%提醒程序,要在該處打印一個(gè)變量,d表明把變量作為十進(jìn)制整數(shù)打印。printf()函數(shù)名中的f提醒用戶,這是一種格式化打印函數(shù)。printf()函數(shù)有多種打印變量的格式,包括小數(shù)和十六進(jìn)制整數(shù)。后面章節(jié)在介紹數(shù)據(jù)類型時(shí),會(huì)詳細(xì)介紹相關(guān)內(nèi)容。

8.return語(yǔ)句

return 0;

return語(yǔ)句[3]是程序清單2.1的最后一條語(yǔ)句。int main(void)中的int表明main()函數(shù)應(yīng)返回一個(gè)整數(shù)。C標(biāo)準(zhǔn)要求main()這樣做。有返回值的C函數(shù)要有return語(yǔ)句。該語(yǔ)句以return關(guān)鍵字開(kāi)始,后面是待返回的值,并以分號(hào)結(jié)尾。如果遺漏 main()函數(shù)中的 return 語(yǔ)句,程序在運(yùn)行至最外面的右花括號(hào)(})時(shí)會(huì)返回0。因此,可以省略main()函數(shù)末尾的return語(yǔ)句。但是,不要在其他有返回值的函數(shù)中漏掉它。因此,強(qiáng)烈建議讀者養(yǎng)成在 main()函數(shù)中保留 return 語(yǔ)句的好習(xí)慣。在這種情況下,可將其看作是統(tǒng)一代碼風(fēng)格。但對(duì)于某些操作系統(tǒng)(包括Linux和UNIX),return語(yǔ)句有實(shí)際的用途。第11章再詳述這個(gè)主題。

2.3 簡(jiǎn)單程序的結(jié)構(gòu)

在看過(guò)一個(gè)具體的程序示例后,我們來(lái)了解一下C程序的基本結(jié)構(gòu)。程序由一個(gè)或多個(gè)函數(shù)組成,必須有 main()函數(shù)。函數(shù)由函數(shù)頭和函數(shù)體組成。函數(shù)頭包括函數(shù)名、傳入該函數(shù)的信息類型和函數(shù)的返回類型。通過(guò)函數(shù)名后的圓括號(hào)可識(shí)別出函數(shù),圓括號(hào)里可能為空,可能有參數(shù)。函數(shù)體被花括號(hào)括起來(lái),由一系列語(yǔ)句、聲明組成,如圖2.4所示。本章的程序示例中有一條聲明,聲明了程序使用的變量名和類型。然后是一條賦值表達(dá)式語(yǔ)句,變量被賦給一個(gè)值。接下來(lái)是3條printf()語(yǔ)句[4],調(diào)用printf()函數(shù)3次。最后,main()以return語(yǔ)句結(jié)束。

簡(jiǎn)而言之,一個(gè)簡(jiǎn)單的C程序的格式如下:

#include <stdio.h>

int main(void)

{

? ? 語(yǔ)句

? ? return 0;

}

(大部分語(yǔ)句都以分號(hào)結(jié)尾。)

2.4 提高程序可讀性的技巧

編寫可讀性高的程序是良好的編程習(xí)慣。可讀性高的程序更容易理解,以后也更容易修改和更正。提高程序的可讀性還有助于你理清編程思路。

前面介紹過(guò)兩種提高程序可讀性的技巧:選擇有意義的函數(shù)名和寫注釋。注意,使用這兩種技巧時(shí)應(yīng)相得益彰,避免重復(fù)啰嗦。如果變量名是width,就不必寫注釋說(shuō)明該變量表示寬度,但是如果變量名是video_routine_4,就要解釋一下該變量名的含義。

提高程序可讀性的第3個(gè)技巧是:在函數(shù)中用空行分隔概念上的多個(gè)部分。例如,程序清單2.1中用空行把聲明部分和程序的其他部分區(qū)分開(kāi)來(lái)。C語(yǔ)言并未規(guī)定一定要使用空行,但是多使用空行能提高程序的可讀性。

提高程序可讀性的第4個(gè)技巧是:每條語(yǔ)句各占一行。同樣,這也不是C語(yǔ)言的要求。C語(yǔ)言的格式比較自由,可以把多條語(yǔ)句放在一行,也可以每條語(yǔ)句獨(dú)占一行。下面的語(yǔ)句都沒(méi)問(wèn)題,但是不好看:

int main( void ) { int four; four

=

4

;

printf(

? ? ? ? ?"%d\n",

four); return 0;}

分號(hào)告訴編譯器一條語(yǔ)句在哪里結(jié)束、下一條語(yǔ)句在哪里開(kāi)始。如果按照本章示例的約定來(lái)編寫代碼(見(jiàn)圖2.5),程序的邏輯會(huì)更清晰。

2.5 進(jìn)一步使用C

本章的第1個(gè)程序相當(dāng)簡(jiǎn)單,下面的程序清單2.2也不太難。

程序清單2.2 fathm_ft.c程序

// fathm_ft.c -- 把2英尋轉(zhuǎn)換成英尺

#include <stdio.h>

int main(void)

{

? ? int feet, fathoms;

? ? fathoms = 2;

? ? feet = 6 * fathoms;

? ? printf("There are %d feet in %d fathoms!\n", feet, fathoms);

? ? printf("Yes, I said %d feet!\n", 6 * fathoms);

? ? return 0;

}

與程序清單2.1相比,以上代碼有什么新內(nèi)容?這段代碼提供了程序描述,聲明了多個(gè)變量,進(jìn)行了乘法運(yùn)算,并打印了兩個(gè)變量的值。下面我們更詳細(xì)地分析這些內(nèi)容。

2.5.1 程序說(shuō)明

程序在開(kāi)始處有一條注釋(使用新的注釋風(fēng)格),給出了文件名和程序的目的。寫這種程序說(shuō)明很簡(jiǎn)單、不費(fèi)時(shí),而且在以后瀏覽或打印程序時(shí)很有幫助。

2.5.2 多條聲明

接下來(lái),程序在一條聲明中聲明了兩個(gè)變量,而不是一個(gè)變量。為此,

要在聲明中用逗號(hào)隔開(kāi)兩個(gè)變量(feet和fathoms)。也就是說(shuō),

int feet, fathoms;

int feet;

int fathoms;

等價(jià)。

2.5.3 乘法

然后,程序進(jìn)行了乘法運(yùn)算。利用計(jì)算機(jī)強(qiáng)大的計(jì)算能力來(lái)計(jì)算 6 乘以2。C 語(yǔ)言和許多其他語(yǔ)言一樣,用*表示乘法。因此,語(yǔ)句

feet = 6 * fathoms;

的意思是“查找變量fathoms的值,用6乘以該值,并把計(jì)算結(jié)果賦給變量feet”。

2.5.4 打印多個(gè)值

最后,程序以新的方式使用printf()函數(shù)。如果編譯并運(yùn)行該程序,輸出應(yīng)該是這樣:

There are 12 feet in 2 fathoms!

Yes, I said 12 feet!

程序的第1個(gè)printf()中進(jìn)行了兩次替換。雙引號(hào)號(hào)后面的第1個(gè)變量(feet)替換了雙引號(hào)中的第1個(gè)%d;雙引號(hào)號(hào)后面的第2個(gè)變量(fathoms)替換了雙引號(hào)中的第2個(gè)%d。注意,待輸出的變量列于雙引號(hào)的后面。還要注意,變量之間要用逗號(hào)隔開(kāi)。

第2個(gè)printf()函數(shù)說(shuō)明待打印的值不一定是變量,只要可求值得出合適類型值的項(xiàng)即可,如6 *fathoms。

該程序涉及的范圍有限,但它是把英尋[5]轉(zhuǎn)換成英尺程序的核心部分。我們還需要把其他值通過(guò)交互的方式賦給feet,其方法將在后面章節(jié)中介紹。

2.6 多個(gè)函數(shù)

到目前為止,介紹的幾個(gè)程序都只使用了printf()函數(shù)。程序清單2.3演示了除main()以外,如何把自己的函數(shù)加入程序中。

程序清單2.3 two_func.c程序

//* two_func.c -- 一個(gè)文件中包含兩個(gè)函數(shù) */

#include <stdio.h>

void butler(void); /* ANSI/ISO C函數(shù)原型 */

int main(void)

{

? ? printf("I will summon the butler function.\n");

? ? butler();

? ? printf("Yes. Bring me some tea and writeable DVDs.\n");

? ? return 0;

}

void butler(void) /* 函數(shù)定義開(kāi)始 */

{

? ? printf("You rang, sir?\n");

}

該程序的輸出如下:

I will summon the butler function.

You rang, sir?

Yes.Bring me some tea and writeable DVDs.

butler()函數(shù)在程序中出現(xiàn)了3次。第1次是函數(shù)原型(prototype),告知編譯器在程序中要使用該函數(shù);第 2 次以函數(shù)調(diào)用(function call)的形式出現(xiàn)在 main()中;最后一次出現(xiàn)在函數(shù)定義(function definition)中,函數(shù)定義即是函數(shù)本身的源代碼。下面逐一分析。

C90 標(biāo)準(zhǔn)新增了函數(shù)原型,舊式的編譯器可能無(wú)法識(shí)別(稍后我們將介紹,如果使用這種編譯器應(yīng)該怎么做)。函數(shù)原型是一種聲明形式,告知編譯器正在使用某函數(shù),因此函數(shù)原型也被稱為函數(shù)聲明(function declaration)。函數(shù)原型還指明了函數(shù)的屬性。例如,butler()函數(shù)原型中的第1個(gè)void表明,butler()函數(shù)沒(méi)有返回值(通常,被調(diào)函數(shù)會(huì)向主調(diào)函數(shù)返回一個(gè)值,但是 bulter()函數(shù)沒(méi)有)。第 2 個(gè) void (butler(void)中的 void)的意思是 butler()函數(shù)不帶參數(shù)。因此,當(dāng)編譯器運(yùn)行至此,會(huì)檢查butler()是否使用得當(dāng)。注意,void在這里的意思是“空的”,而不是“無(wú)效”。

早期的C語(yǔ)言支持一種更簡(jiǎn)單的函數(shù)聲明,只需指定返回類型,不用描述參數(shù):

void butler();

早期的C代碼中的函數(shù)聲明就類似上面這樣,不是現(xiàn)在的函數(shù)原型。C90、C99 和C11 標(biāo)準(zhǔn)都承認(rèn)舊版本的形式,但是也表明了會(huì)逐漸淘汰這種過(guò)時(shí)的寫法。如果要使用以前寫的 C代碼,就需要把舊式聲明轉(zhuǎn)換成函數(shù)原型。本書在后面的章節(jié)會(huì)繼續(xù)介紹函數(shù)原型的相關(guān)內(nèi)容。

接下來(lái)我們繼續(xù)分析程序。在 main()中調(diào)用 butler()很簡(jiǎn)單,寫出函數(shù)名和圓括號(hào)即可。當(dāng)butler()執(zhí)行完畢后,程序會(huì)繼續(xù)執(zhí)行main()中的下一條語(yǔ)句。

程序的最后部分是 butler()函數(shù)的定義,其形式和 main()相同,都包含函數(shù)頭和用花括號(hào)括起來(lái)的函數(shù)體。函數(shù)頭重述了函數(shù)原型的信息:bulter()不帶任何參數(shù),且沒(méi)有返回值。如果使用老式編譯器,請(qǐng)去掉圓括號(hào)中的void。

這里要注意,何時(shí)執(zhí)行 butler()函數(shù)取決于它在 main()中被調(diào)用的位置,而不是 butler()的定義在文件中的位置。例如,把 butler()函數(shù)的定義放在 main()定義之前,不會(huì)改變程序的執(zhí)行順序, butler()函數(shù)仍然在兩次printf()調(diào)用之間被調(diào)用。記住,無(wú)論main()在程序文件處于什么位置,所有的C程序都從main()開(kāi)始執(zhí)行。但是,C的慣例是把main()放在開(kāi)頭,因?yàn)樗峁┝顺绦虻幕究蚣堋?/p>

C標(biāo)準(zhǔn)建議,要為程序中用到的所有函數(shù)提供函數(shù)原型。標(biāo)準(zhǔn)include文件(包含文件)為標(biāo)準(zhǔn)庫(kù)函數(shù)提供可函數(shù)原型。例如,在C標(biāo)準(zhǔn)中,stdio.h文件包含了printf()的函數(shù)原型。第6章最后一個(gè)示例演示了如何使用帶返回值的函數(shù),第9章將詳細(xì)全面地介紹函數(shù)。

2.7 調(diào)試程序

現(xiàn)在,你可以編寫一個(gè)簡(jiǎn)單的 C 程序,但是可能會(huì)犯一些簡(jiǎn)單的錯(cuò)誤。程序的錯(cuò)誤通常叫做 bug,找出并修正錯(cuò)誤的過(guò)程叫做調(diào)試(debug)。程序清單2.4是一個(gè)有錯(cuò)誤的程序,看看你能找出幾處。

程序清單2.4 nogood.c程序

/* nogood.c -- 有錯(cuò)誤的程序 */

#include <stdio.h>

int main(void)

(

? ? int n, int n2, int n3;

? ? /* 該程序有多處錯(cuò)誤

? ? n = 5;

? ? n2 = n * n;

? ? n3 = n2 * n2;

? ? printf("n = %d, n squared = %d, n cubed = %d\n", n,n2, n3)

? ? return 0;

)

2.7.1 語(yǔ)法錯(cuò)誤

程序清單 2.4 中有多處語(yǔ)法錯(cuò)誤。如果不遵循 C 語(yǔ)言的規(guī)則就會(huì)犯語(yǔ)法錯(cuò)誤。這類似于英文中的語(yǔ)法錯(cuò)誤。例如,看看這個(gè)句子:Bugs frustrate be can[6]。該句子中的英文單詞都是有效的單詞(即,拼寫正確),但是并未按照正確的順序組織句子,而且用詞也不妥。C語(yǔ)言的語(yǔ)法錯(cuò)誤指的是,把有效的C符號(hào)放在錯(cuò)誤的地方。

nogood.c程序中有哪些錯(cuò)誤?其一,main()函數(shù)體使用圓括號(hào)來(lái)代替花括號(hào)。這就是把C符號(hào)用錯(cuò)了地方。其二,變量聲明應(yīng)該這樣寫:

int n, n2, n3;

或者,這樣寫:

int n;

int n2;

int n3;

其三,main()中的注釋末尾漏掉了*/(另一種修改方案是,用//替換/*)。最后,printf()語(yǔ)句末尾漏掉了分號(hào)。

如何發(fā)現(xiàn)程序的語(yǔ)法錯(cuò)誤?首先,在編譯之前,瀏覽源代碼看是否能發(fā)現(xiàn)一些明顯的錯(cuò)誤。接下來(lái),查看編譯器是否發(fā)現(xiàn)錯(cuò)誤,檢查程序的語(yǔ)法錯(cuò)誤是它的工作之一。在編譯程序時(shí),編譯器發(fā)現(xiàn)錯(cuò)誤會(huì)報(bào)告錯(cuò)誤信息,指出每一處錯(cuò)誤的性質(zhì)和具體位置。

盡管如此,編譯器也有出錯(cuò)的時(shí)候。也許某處隱藏的語(yǔ)法錯(cuò)誤會(huì)導(dǎo)致編譯器誤判。例如,由于nogood.c程序未正確聲明n2和n3,會(huì)導(dǎo)致編譯器在使用這些變量時(shí)發(fā)現(xiàn)更多問(wèn)題。實(shí)際上,有時(shí)不用把編譯器報(bào)告的所有錯(cuò)誤逐一修正,僅修正第 1 條或前幾處錯(cuò)誤后,錯(cuò)誤信息就會(huì)少很多。繼續(xù)這樣做,直到編譯器不再報(bào)錯(cuò)。編譯器另一個(gè)常見(jiàn)的毛病是,報(bào)錯(cuò)的位置比真正的錯(cuò)誤位置滯后一行。例如,編譯器在編譯下一行時(shí)才會(huì)發(fā)現(xiàn)上一行缺少分號(hào)。因此,如果編譯器報(bào)錯(cuò)某行缺少分號(hào),請(qǐng)檢查上一行。

2.7.2 語(yǔ)義錯(cuò)誤

語(yǔ)義錯(cuò)誤是指意思上的錯(cuò)誤。例如,考慮這個(gè)句子:Scornful derivatives sing greenly(輕蔑的衍生物不熟練地唱歌)。句中的形容詞、名詞、動(dòng)詞和副詞都在正確的位置上,所以語(yǔ)法正確。但是,卻讓人不知所云。在C語(yǔ)言中,如果遵循了C規(guī)則,但是結(jié)果不正確,那就是犯了語(yǔ)義錯(cuò)誤。程序示例中有這樣的錯(cuò)誤:

n3 = n2 * n2;

此處,n3原意表示n的3次方,但是代碼中的n3被設(shè)置成n的4次方(n2 =n * n)。

編譯器無(wú)法檢測(cè)語(yǔ)義錯(cuò)誤,因?yàn)檫@類錯(cuò)誤并未違反 C語(yǔ)言的規(guī)則。編譯器無(wú)法了解你的真正意圖,所以你只能自己找出這些錯(cuò)誤。例如,假設(shè)你修正了程序的語(yǔ)法錯(cuò)誤,程序應(yīng)該如程序清單2.5所示:

程序清單2.5 stillbad.c程序

/* stillbad.c -- 修復(fù)了語(yǔ)法錯(cuò)誤的程序 */

#include <stdio.h>

int main(void)

{

? ? int n, n2, n3;

? ? /* 該程序有一個(gè)語(yǔ)義錯(cuò)誤 */

? ? n = 5;

? ? n2 = n * n;

? ? n3 = n2 * n2;

? ? printf("n = %d, n squared = %d, n cubed = %d\n", n,n2, n3);

? ? return 0;

}

該程序的輸出如下:

n = 5, n squared = 25, n cubed = 625

如果對(duì)簡(jiǎn)單的立方比較熟悉,就會(huì)注意到 625 不對(duì)。下一步是跟蹤程序的執(zhí)行步驟,找出程序如何得出這個(gè)答案。對(duì)于本例,通過(guò)查看代碼就會(huì)發(fā)現(xiàn)其中的錯(cuò)誤,但是,還應(yīng)該學(xué)習(xí)更系統(tǒng)的方法。方法之一是,把自己想象成計(jì)算機(jī),跟著程序的步驟一步一步地執(zhí)行。下面,我們來(lái)試試這種方法。

main()函數(shù)體一開(kāi)始就聲明了3個(gè)變量:n、n2、n3。你可以畫出3個(gè)盒子并把變量名寫在盒子上來(lái)模擬這種情況(見(jiàn)圖2.6)。接下來(lái),程序把5賦給變量n。你可以在標(biāo)簽為n的盒子里寫上5。接著,程序把n和n相乘,并把乘積賦給n2。因此,查看標(biāo)簽為n的盒子,其值是5,5乘以5得25,于是把25放進(jìn)標(biāo)簽為 n2 的盒子里。為了模擬下一條語(yǔ)句(n3 = n2 * n2),查看 n2 盒子,發(fā)現(xiàn)其值是 25。25乘以25得625,把625放進(jìn)標(biāo)簽為n3的盒子。原來(lái)如此!程序中計(jì)算的是n2的平方,不是用n2乘以n得到n的3次方。

對(duì)于上面的程序示例,檢查程序的過(guò)程可能過(guò)于繁瑣。但是,用這種方法一步一步查看程序的執(zhí)行情況,通常是發(fā)現(xiàn)程序問(wèn)題所在的良方。

2.7.3 程序狀態(tài)

通過(guò)逐步跟蹤程序的執(zhí)行步驟,并記錄每個(gè)變量,便可監(jiān)視程序的狀態(tài)。程序狀態(tài)(program state)是在程序的執(zhí)行過(guò)程中,某給定點(diǎn)上所有變量值的集合。它是計(jì)算機(jī)當(dāng)前狀態(tài)的一個(gè)快照。

我們剛剛討論了一種跟蹤程序狀態(tài)的方法:自己模擬計(jì)算機(jī)逐步執(zhí)行程序。但是,如果程序中有10000次循環(huán),這種方法恐怕行不通。不過(guò),你可以跟蹤一小部分循環(huán),看看程序是否按照預(yù)期的方式執(zhí)行。另外,還要考慮一種情況:你很可能按照自己所想去執(zhí)行程序,而不是根據(jù)實(shí)際寫出來(lái)的代碼去執(zhí)行。因此,要盡量忠實(shí)代碼來(lái)模擬。

定位語(yǔ)義錯(cuò)誤的另一種方法是:在程序中的關(guān)鍵點(diǎn)插入額外的 printf()語(yǔ)句,以監(jiān)視制定變量值的變化。通過(guò)查看值的變化可以了解程序的執(zhí)行情況。對(duì)程序的執(zhí)行滿意后,便可刪除額外的 printf()語(yǔ)句,然后重新編譯。

檢測(cè)程序狀態(tài)的第3種方法是使用調(diào)試器。調(diào)試器(debugger)是一種程序,讓你一步一步運(yùn)行另一個(gè)程序,并檢查該程序變量的值。調(diào)試器有不同的使用難度和復(fù)雜度。較高級(jí)的調(diào)試器會(huì)顯示正在執(zhí)行的源代碼行號(hào)。這在檢查有多條執(zhí)行路徑的程序時(shí)很方便,因?yàn)楹苋菀字勒趫?zhí)行哪條路徑。如果你的編譯器自帶調(diào)試器,現(xiàn)在可以花點(diǎn)時(shí)間學(xué)會(huì)怎么使用它。例如,試著調(diào)試一下程序清單2.4。

2.8 關(guān)鍵字和保留標(biāo)識(shí)符

關(guān)鍵字是C語(yǔ)言的詞匯。它們對(duì)C而言比較特殊,不能用它們作為標(biāo)識(shí)符(如,變量名)。許多關(guān)鍵字用于指定不同的類型,如 int。還有一些關(guān)鍵字(如,if)用于控制程序中語(yǔ)句的執(zhí)行順序。在表 2.2 中所列的C語(yǔ)言關(guān)鍵字中,粗體表示的是C90標(biāo)準(zhǔn)新增的關(guān)鍵字,斜體表示的C99標(biāo)準(zhǔn)新增的關(guān)鍵字,粗斜體表示的是C11標(biāo)準(zhǔn)新增的關(guān)鍵字。

如果使用關(guān)鍵字不當(dāng)(如,用關(guān)鍵字作為變量名),編譯器會(huì)將其視為語(yǔ)法錯(cuò)誤。還有一些保留標(biāo)識(shí)符(reserved identifier),C語(yǔ)言已經(jīng)指定了它們的用途或保留它們的使用權(quán),如果你使用這些標(biāo)識(shí)符來(lái)表示其他意思會(huì)導(dǎo)致一些問(wèn)題。因此,盡管它們也是有效的名稱,不會(huì)引起語(yǔ)法錯(cuò)誤,也不能隨便使用。保留標(biāo)識(shí)符包括那些以下劃線字符開(kāi)頭的標(biāo)識(shí)符和標(biāo)準(zhǔn)庫(kù)函數(shù)名,如printf()。

2.9 關(guān)鍵概念

編程是一件富有挑戰(zhàn)性的事情。程序員要具備抽象和邏輯的思維,并謹(jǐn)慎地處理細(xì)節(jié)問(wèn)題(編譯器會(huì)強(qiáng)迫你注意細(xì)節(jié)問(wèn)題)。平時(shí)和朋友交流時(shí),可能用錯(cuò)幾個(gè)單詞,犯一兩個(gè)語(yǔ)法錯(cuò)誤,或者說(shuō)幾句不完整的句子,但是對(duì)方能明白你想說(shuō)什么。而編譯器不允許這樣,對(duì)它而言,幾乎正確仍然是錯(cuò)誤。

編譯器不會(huì)在下面講到的概念性問(wèn)題上幫助你。因此,本書在這一章中介紹一些關(guān)鍵概念幫助讀者彌補(bǔ)這部分的內(nèi)容。

在本章中,讀者的目標(biāo)應(yīng)該是理解什么是C程序??梢园殉绦蚩醋魇悄阆M?jì)算機(jī)如何完成任務(wù)的描述。編譯器負(fù)責(zé)處理一些細(xì)節(jié)工作,例如把你要計(jì)算機(jī)完成的任務(wù)轉(zhuǎn)換成底層的機(jī)器語(yǔ)言(如果從量化方面來(lái)解釋編譯器所做的工作,它可以把1KB的源文件創(chuàng)建成60KB的可執(zhí)行文件;即使是一個(gè)很簡(jiǎn)單的C程序也要用大量的機(jī)器語(yǔ)言來(lái)表示)。由于編譯器不具有真正的智能,所以你必須用編譯器能理解的術(shù)語(yǔ)表達(dá)你的意圖,這些術(shù)語(yǔ)就是C語(yǔ)言標(biāo)準(zhǔn)規(guī)定的形式規(guī)則(盡管有些約束,但總比直接用機(jī)器語(yǔ)言方便得多)。

編譯器希望接收到特定格式的指令,我們?cè)诒菊乱呀?jīng)介紹過(guò)。作為程序員的任務(wù)是,在符合 C標(biāo)準(zhǔn)的編譯器框架中,表達(dá)你希望程序應(yīng)該如何完成任務(wù)的想法。

2.10 本章小結(jié)

C程序由一個(gè)或多個(gè)C函數(shù)組成。每個(gè)C程序必須包含一個(gè)main()函數(shù),這是C程序要調(diào)用的第1個(gè)函數(shù)。簡(jiǎn)單的函數(shù)由函數(shù)頭和后面的一對(duì)花括號(hào)組成,花括號(hào)中是由聲明、語(yǔ)句組成的函數(shù)體。

在C語(yǔ)言中,大部分語(yǔ)句都以分號(hào)結(jié)尾。聲明為變量創(chuàng)建變量名和標(biāo)識(shí)該變量中儲(chǔ)存的數(shù)據(jù)類型。變量名是一種標(biāo)識(shí)符。賦值表達(dá)式語(yǔ)句把值賦給變量,或者更一般地說(shuō),把值賦給存儲(chǔ)空間。函數(shù)表達(dá)式語(yǔ)句用于調(diào)用指定的已命名函數(shù)。調(diào)用函數(shù)執(zhí)行完畢后,程序會(huì)返回到函數(shù)調(diào)用后面的語(yǔ)句繼續(xù)執(zhí)行。

printf()函數(shù)用于輸出想要表達(dá)的內(nèi)容和變量的值。

一門語(yǔ)言的語(yǔ)法是一套規(guī)則,用于管理語(yǔ)言中各有效語(yǔ)句組合在一起的方式。語(yǔ)句的語(yǔ)義是語(yǔ)句要表達(dá)的意思。編譯器可以檢測(cè)出語(yǔ)法錯(cuò)誤,但是程序里的語(yǔ)義錯(cuò)誤只有在編譯完之后才能從程序的行為中表現(xiàn)出來(lái)。檢查程序是否有語(yǔ)義錯(cuò)誤要跟蹤程序的狀態(tài),即程序每執(zhí)行一步后所有變量的值。

最后,關(guān)鍵字是C語(yǔ)言的詞匯。

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

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

1.C語(yǔ)言的基本模塊是什么?

2.什么是語(yǔ)法錯(cuò)誤?寫出一個(gè)英語(yǔ)例子和C語(yǔ)言例子。

3.什么是語(yǔ)義錯(cuò)誤?寫出一個(gè)英語(yǔ)例子和C語(yǔ)言例子。

4.Indiana Sloth編寫了下面的程序,并征求你的意見(jiàn)。請(qǐng)幫助他評(píng)定。

include studio.h

int main{void} /* 該程序打印一年有多少周 /*

(

? ? int s

? ? s := 56;

? ? print(There are s weeks in a year.);

? ? return 0;

5.假設(shè)下面的4個(gè)例子都是完整程序中的一部分,它們都輸出什么結(jié)

果?

a. printf("Baa Baa Black Sheep.");

? ? printf("Have you any wool?\n");

b. printf("Begone!\nO creature of lard!\n");

c.printf("What?\nNo/nfish?\n");

d.int num;

? ?num = 2;

? ?printf("%d + %d = %d", num, num, num + num);

6.在main、int、function、char、=中,哪些是C語(yǔ)言的關(guān)鍵字?

7.如何以下面的格式輸出變量words和lines的值(這里,3020和350代表兩個(gè)變量的值)?

There were 3020 words and 350 lines.

8.考慮下面的程序:

????#include <stdio.h>

????int main(void)

????{

? ????? int a, b;

? ????? a = 5;

? ????? b = 2; /* 第7行 */

? ????? b = a; /* 第8行 */

? ????? a = b; /* 第9行 */

? ????? printf("%d %d\n", b, a);

? ????? return 0;

????}

請(qǐng)問(wèn),在執(zhí)行完第7、第8、第9行后,程序的狀態(tài)分別是什么?

9.考慮下面的程序:

????#include <stdio.h>

????int main(void)

????{

? ????? int x, y;

? ? ????x = 10;

? ? ????y = 5; /* 第7行 */

? ? ????y = x + y; /*第8行*/

? ? ????x = x*y; /*第9行*/

? ? ????printf("%d %d\n", x, y);

? ? ????return 0;

????}

請(qǐng)問(wèn),在執(zhí)行完第7、第8、第9行后,程序的狀態(tài)分別是什么?

2.12 編程練習(xí)

紙上得來(lái)終覺(jué)淺,絕知此事要躬行。讀者應(yīng)該試著編寫一兩個(gè)簡(jiǎn)單的程序,體會(huì)一下編寫程序是否和閱讀本章介紹的這樣輕松。題目中會(huì)給出一些建議,但是應(yīng)該盡量自己思考這些問(wèn)題。一些編程答案練習(xí)的答案可在出版商網(wǎng)站獲取。

1.編寫一個(gè)程序,調(diào)用一次 printf()函數(shù),把你的姓名打印在一行。再調(diào)用一次 printf()函數(shù),把你的姓名分別打印在兩行。然后,再調(diào)用兩次printf()函數(shù),把你的姓名打印在一行。輸出應(yīng)如下所示(當(dāng)然要把示例的內(nèi)容換成你的姓名):

2.編寫一個(gè)程序,打印你的姓名和地址。

3.編寫一個(gè)程序把你的年齡轉(zhuǎn)換成天數(shù),并顯示這兩個(gè)值。這里不用考慮閏年的問(wèn)題。

4.編寫一個(gè)程序,生成以下輸出:

For he's a jolly good fellow!

For he's a jolly good fellow!

For he's a jolly good fellow!

Which nobody can deny!

除了 main()函數(shù)以外,該程序還要調(diào)用兩個(gè)自定義函數(shù):一個(gè)名為jolly(),用于打印前 3 條消息,調(diào)用一次打印一條;另一個(gè)函數(shù)名為deny(),打印最后一條消息。

5.編寫一個(gè)程序,生成以下輸出:

Brazil, Russia, India, China

India, China,

Brazil, Russia

除了main()以外,該程序還要調(diào)用兩個(gè)自定義函數(shù):一個(gè)名為br(),調(diào)用一次打印一次“Brazil, Russia”;另一個(gè)名為ic(),調(diào)用一次打印一次“India,China”。其他內(nèi)容在main()函數(shù)中完成。

6.編寫一個(gè)程序,創(chuàng)建一個(gè)整型變量toes,并將toes設(shè)置為10。程序中還要計(jì)算toes的兩倍和toes的平方。該程序應(yīng)打印3個(gè)值,并分別描述以示區(qū)分。

7.許多研究表明,微笑益處多多。編寫一個(gè)程序,生成以下格式的輸出:

Smile!Smile!Smile!

Smile!Smile!

Smile!

該程序要定義一個(gè)函數(shù),該函數(shù)被調(diào)用一次打印一次“Smile!”,根據(jù)程序的需要使用該函數(shù)。

8.在C語(yǔ)言中,函數(shù)可以調(diào)用另一個(gè)函數(shù)。編寫一個(gè)程序,調(diào)用一個(gè)名為one_three()的函數(shù)。該函數(shù)在一行打印單詞“one”,再調(diào)用第2個(gè)函數(shù)two(),然后在另一行打印單詞“three”。two()函數(shù)在一行顯示單詞“two”。main()函數(shù)在調(diào)用 one_three()函數(shù)前要打印短語(yǔ)“starting now:”,并在調(diào)用完畢后顯示短語(yǔ)“done!”。因此,該程序的輸出應(yīng)如下所示:

starting now:

one

two

three

done!

[1].原書圖中敘述有誤。根據(jù)C11標(biāo)準(zhǔn),C語(yǔ)言有6種語(yǔ)句,已在圖中更正。——譯者注

[2].C語(yǔ)言是通過(guò)賦值運(yùn)算符而不是賦值語(yǔ)句完成賦值操作。根據(jù)C標(biāo)準(zhǔn),C語(yǔ)言并沒(méi)有所謂的“賦值語(yǔ)句”,本書及一些其他書籍中提到的“賦值語(yǔ)句”實(shí)際上是表達(dá)式語(yǔ)句(C語(yǔ)言的6種基本語(yǔ)句之一)。本書把“賦值語(yǔ)句”均譯為“賦值表達(dá)式語(yǔ)句”,以提醒初學(xué)者注意?!g者注

[3].在C語(yǔ)言中,return語(yǔ)句是一種跳轉(zhuǎn)語(yǔ)句。——譯者注

[4].市面上許多書籍(包括本書)都把這種語(yǔ)句叫作“函數(shù)調(diào)用語(yǔ)句”,但是歷年的C標(biāo)準(zhǔn)中從來(lái)沒(méi)有函數(shù)調(diào)用語(yǔ)句!值得一提的是,函數(shù)調(diào)用本身是一個(gè)表達(dá)式,圓括號(hào)是運(yùn)算符,圓括號(hào)左邊的函數(shù)名是運(yùn)算對(duì)象。在C11標(biāo)準(zhǔn)中,這樣的表達(dá)式是一種后綴表達(dá)式。在表達(dá)式末尾加上分號(hào),就成了表達(dá)式語(yǔ)句。請(qǐng)初學(xué)者注意,這樣的“函數(shù)調(diào)用語(yǔ)句”實(shí)質(zhì)是表達(dá)式語(yǔ)句。本書的錯(cuò)誤之處已在翻譯過(guò)程中更正?!g者注

[5].英尋,也稱為尋。航海用的深度單位,1英尋=6英尺=1.8米,通常用在海圖上測(cè)量水深?!g者注

[6].要理解該句子存在語(yǔ)法錯(cuò)誤,需要具備基本的英文語(yǔ)法知識(shí)?!g者注


【轉(zhuǎn)載】C Primer Plus(第6版):第2章 C語(yǔ)言概述的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
卢湾区| 江都市| 安达市| 陆川县| 浙江省| 桃源县| 溆浦县| 汨罗市| 荣成市| 冷水江市| 桑植县| 双峰县| 遵化市| 泰州市| 钟祥市| 高邮市| 雅江县| 江山市| 临邑县| 新民市| 满城县| 涪陵区| 南陵县| 峡江县| 武汉市| 四平市| 阿尔山市| 鹿泉市| 老河口市| 朔州市| 田东县| 佛冈县| 乌拉特后旗| 和政县| 鄱阳县| 庆安县| 弥渡县| 大竹县| 儋州市| 新民市| 哈尔滨市|