官术网_书友最值得收藏!

第2章 Cx51程序設(shè)計基礎(chǔ)

Keil Cx51是一種專為8051單片機(jī)設(shè)計的高級語言C編譯器,支持符合ANSI標(biāo)準(zhǔn)的C語言進(jìn)行程序設(shè)計,同時針對8051單片機(jī)自身特點(diǎn)作了一些特殊擴(kuò)展。為了幫助以前慣于使用匯編語言編程的單片機(jī)用戶盡快掌握Cx51編程技術(shù),本章對C語言的一些基本知識結(jié)合Cx51特點(diǎn)進(jìn)行闡述。

2.1 標(biāo)識符與關(guān)鍵字

C語言的標(biāo)識符是用來標(biāo)識源程序中某個對象名字的。這些對象可以是函數(shù)、變量、常量、數(shù)組、數(shù)據(jù)類型、存儲方式、語句等。一個標(biāo)識符由字母、數(shù)字和下劃線等組成,第一個字符必須是字母或下劃線。C語言是對大小寫字母敏感的,如“max”與“MAX”是兩個完全不同的標(biāo)識符。程序中對于標(biāo)識符的命名應(yīng)當(dāng)簡潔明了,含義清晰,便于閱讀理解,如用標(biāo)識符“max”表示最大值,用“TIMER0”表示定時器0等。

關(guān)鍵字是一類具有固定名稱和特定含義的特殊標(biāo)識符,有時又稱為保留字。在編寫C語言源程序時一般不允許將關(guān)鍵字另作他用,換句話說就是對于標(biāo)識符的命名不要與關(guān)鍵字相同。與其他計算機(jī)語言相比,C語言的關(guān)鍵字是比較少的,ANSI C標(biāo)準(zhǔn)一共規(guī)定了32個關(guān)鍵字,表2-1按用途列出了ANSI C標(biāo)準(zhǔn)的關(guān)鍵字。

表2-1 ANSI C標(biāo)準(zhǔn)的關(guān)鍵字

Keil Cx51編譯器除了支持ANSI C標(biāo)準(zhǔn)的關(guān)鍵字以外,還根據(jù)8051單片機(jī)自身特點(diǎn)擴(kuò)展了如表2-2所示的關(guān)鍵字。

表2-2 Keil Cx51編譯器的擴(kuò)展關(guān)鍵字

2.2 Cx51程序設(shè)計的基本語法

雖然C語言對語法的限制不太嚴(yán)格,用戶在編寫程序時有較大的自由,但它畢竟還是一種程序設(shè)計語言,與其他計算機(jī)語言一樣,采用C語言進(jìn)行程序設(shè)計時,仍需要遵從一定的語法規(guī)則。

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

任何程序設(shè)計都離不開對于數(shù)據(jù)的處理,一個程序如果沒有數(shù)據(jù),它就無法工作。數(shù)據(jù)在計算機(jī)內(nèi)存中的存放情況由數(shù)據(jù)結(jié)構(gòu)決定。C語言的數(shù)據(jù)結(jié)構(gòu)是以數(shù)據(jù)類型出現(xiàn)的,數(shù)據(jù)類型可分為基本數(shù)據(jù)類型和復(fù)雜數(shù)據(jù)類型,復(fù)雜數(shù)據(jù)類型由基本數(shù)據(jù)類型構(gòu)造而成。C語言中的基本數(shù)據(jù)類型有char,int,short,long,float和double。對于Cx51編譯器來說,short類型與int類型相同,double類型與float類型相同。

1.char:字符型

有signed char(帶符號數(shù))和unsigned char(無符號數(shù))之分,默認(rèn)值為signed char。它們的長度均為一個字節(jié),用于存放一個單字節(jié)的數(shù)據(jù)。對于singed char類型數(shù)據(jù),其字節(jié)中的最高位表示該數(shù)據(jù)的符號,“0”表示正數(shù),“1”表示負(fù)數(shù)。負(fù)數(shù)用補(bǔ)碼表示,數(shù)值的表示范圍是-128~+127;對于unsigned char類型數(shù)據(jù),其字節(jié)中的所有位均用來表示數(shù)據(jù)的數(shù)值,數(shù)值的表示范圍是0~255。

2.int:整型

有signed int和unsigned int之分,默認(rèn)值為signed int。它們的長度均為兩個字節(jié),用于存放一個雙字節(jié)的數(shù)據(jù)。signed int是有符號整型數(shù),字節(jié)中的最高位表示數(shù)據(jù)的符號,“0”表示正數(shù),“1”表示負(fù)數(shù)。所能表示的數(shù)值范圍是-32768~+32767。unsigned int是無符號整型數(shù),所能表示的數(shù)值范圍是0~65535。

3.long:長整型

有signed long和unsigned long之分,默認(rèn)值為signed long。它們的長度均為四個字節(jié)。signed long是有符號的長整型數(shù)據(jù),字節(jié)中的最高位表示數(shù)據(jù)的符號,“0”表示正數(shù),“1”表示負(fù)數(shù),數(shù)值的表示范圍是-2147 483648~+2147 483647。unsigned long是無符號長整型數(shù)據(jù),數(shù)值的表示范圍是0~4294 967295。

4.float:浮點(diǎn)型

它是符合IEEE-754標(biāo)準(zhǔn)的單精度浮點(diǎn)型數(shù)據(jù),在十進(jìn)制數(shù)中具有7位有效數(shù)字。float類型數(shù)據(jù)占用四個字節(jié)(32位二進(jìn)制數(shù)),在內(nèi)存中的存放格式如下:

其中,S為符號位,“0”表示正,“1”表示負(fù)。E為階碼,占用8位二進(jìn)制數(shù),存放在兩個字節(jié)中。注意,階碼E值是以2為底的指數(shù)再加上偏移量127,這樣處理的目的是為了避免出現(xiàn)負(fù)的階碼值,而指數(shù)是可正可負(fù)的。階碼E的正常取值范圍是1~254,從而實際指數(shù)的取值范圍為-126~+127。M為尾數(shù)的小數(shù)部分,用23位二進(jìn)制數(shù)表示,存放在三個字節(jié)中。尾數(shù)的整數(shù)部分永遠(yuǎn)為1,因此不予保存,但它是隱含存在的。小數(shù)點(diǎn)位于隱含的整數(shù)位“1”的后面。一個浮點(diǎn)數(shù)的數(shù)值范圍是(-1)S×2E-127×(1.M)。

例如,浮點(diǎn)數(shù)-12.5=0xC1480000,在內(nèi)存中的存放格式為:

需要指出的是,對于浮點(diǎn)型數(shù)據(jù)除了有正常數(shù)值之外,還可能出現(xiàn)非正常數(shù)值。根據(jù)IEEE標(biāo)準(zhǔn),當(dāng)浮點(diǎn)型數(shù)據(jù)取以下數(shù)值(16進(jìn)制數(shù))時即為非正常值:

0xFFFFFFFF非數(shù)(NaN)

0x7F800000正溢出(+INF)

0xFF800000負(fù)溢出(-INF)

另外,由于8051單片機(jī)不包括捕獲浮點(diǎn)運(yùn)算錯誤的中斷向量,因此必須由用戶自己根據(jù)可能出現(xiàn)的錯誤條件用軟件來進(jìn)行適當(dāng)?shù)奶幚怼?/p>

除了以上四種基本數(shù)據(jù)類型之外,還有以下一些數(shù)據(jù)類型。

5.*:指針型

指針型數(shù)據(jù)不同于以上四種基本數(shù)據(jù)類型,它本身是一個變量,但在這個變量中存放的不是普通數(shù)據(jù)而是指向另一個數(shù)據(jù)的地址。指針變量也要占據(jù)一定的內(nèi)存單元,在C51中指針變量的長度一般為1~3個字節(jié)。指針變量也具有類型,其表示方法是在指針符號“*”的前面冠以數(shù)據(jù)類型符號,如char * point1表示point1是一個字符型的指針變量;float* point2表示point2是一個浮點(diǎn)型的指針變量。指針變量的類型表示該指針?biāo)赶虻刂分袛?shù)據(jù)的類型。使用指針型變量可以方便地對8051單片機(jī)各部分物理地址直接進(jìn)行操作。

6.bit:位類型

這是Keil Cx51編譯器的一種擴(kuò)充數(shù)據(jù)類型,利用它可定義一個位變量,但不能定義位指針,也不能定義位數(shù)組。

7.sfr:特殊功能寄存器

這也是Keil Cx51編譯器的一種擴(kuò)充數(shù)據(jù)類型,利用它可以定義8051單片機(jī)的所有內(nèi)部8位特殊功能寄存器。sfr型數(shù)據(jù)占用一個內(nèi)存單元,其取值范圍是0~255。

8.sfr16:16位特殊功能寄存器

它占用兩個內(nèi)存單元,取值范圍是0~65535,利用它可以定義8051單片機(jī)內(nèi)部16位特殊功能寄存器。

9.sbit:可尋址位

這也是Keil Cx51編譯器的一種擴(kuò)充數(shù)據(jù)類型,利用它可以定義8051單片機(jī)內(nèi)部RAM中的可尋址位或特殊功能寄存器中的可尋址位。

例如,采用如下語句:

sfr P0=80H;
sbit FLAG1=P0^1;

可以將8051單片機(jī)P0口地址定義為80H,將P0.1位定義為FLAG1。

表2-3列出了Keil Cx51編譯器能夠識別的數(shù)據(jù)類型。

表2-3 Keil Cx51編譯器能夠識別的數(shù)據(jù)類型

在C語言程序的表達(dá)式或變量賦值運(yùn)算中,有時會出現(xiàn)運(yùn)算對象的數(shù)據(jù)不一致的情況,C語言允許任何標(biāo)準(zhǔn)數(shù)據(jù)類型之間的隱式轉(zhuǎn)換。隱式轉(zhuǎn)換按以下優(yōu)先級別自動進(jìn)行:

bit→char→int→long→float

signed→unsigned

其中箭頭方向僅表示數(shù)據(jù)類型級別的高低,轉(zhuǎn)換時由低向高進(jìn)行,而不是數(shù)據(jù)轉(zhuǎn)換時的順序。例如,將一個bit(位類型)變量賦給一個int(整型變量)時,不需要先將bit型變量轉(zhuǎn)換成char型之后再轉(zhuǎn)換成int型,而是將bit型變量直接轉(zhuǎn)換成int型并完成賦值運(yùn)算。一般來說,如果有幾個不同類型的數(shù)據(jù)同時參加運(yùn)算,先將低級別類型的數(shù)據(jù)轉(zhuǎn)換成高級別類型,再作運(yùn)算處理,并且運(yùn)算結(jié)果為高級別類型數(shù)據(jù)。C語言除了能對數(shù)據(jù)類型作自動的隱式轉(zhuǎn)換之外,還可以采用強(qiáng)制類型轉(zhuǎn)換符“()”對數(shù)據(jù)類型作顯式轉(zhuǎn)換,強(qiáng)制類型轉(zhuǎn)換符“()”的應(yīng)用將在2.2.5節(jié)中介紹。

Keil Cx51編譯器除了能支持以上這些基本數(shù)據(jù)之外,還能支持復(fù)雜的構(gòu)造類型數(shù)據(jù),如結(jié)構(gòu)類型、聯(lián)合類型等。這些復(fù)雜的數(shù)據(jù)類型將在本書第5章詳細(xì)討論。

2.2.2 常量

常量又稱為標(biāo)量,它的值在程序執(zhí)行過程中不能改變。常量的數(shù)據(jù)類型有整型、浮點(diǎn)型、字符型和字符串型等。

1.整型常量

整型常量就是整型常數(shù),可表示為以下幾種形式。

十進(jìn)制整數(shù):如1234,-5678,0等。

十六進(jìn)制整數(shù):ANSI C標(biāo)準(zhǔn)規(guī)定十六進(jìn)制數(shù)據(jù)以0x開頭,數(shù)字為0~9,a~f。如0x123表示十六進(jìn)制數(shù),相當(dāng)于十進(jìn)制數(shù)291。-0x1a表示十六進(jìn)制數(shù),相當(dāng)于十進(jìn)制數(shù)-26。

長整數(shù):在數(shù)字后面加一個字母L就構(gòu)成了長整數(shù),如2048L、0123L、0xff00L等。

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

浮點(diǎn)型常量有十進(jìn)制數(shù)表示形式和指數(shù)表示形式。十進(jìn)制數(shù)表示形式又稱定點(diǎn)表示形式,由數(shù)字和小數(shù)點(diǎn)組成。如0.3141、.3141、314.1、3141.及0.0都是十進(jìn)制數(shù)表示形式的浮點(diǎn)型常量。在這種表示形式中,如果整數(shù)或小數(shù)部分為0可以省略不寫,但必須有小數(shù)點(diǎn)。指數(shù)表示形式為:

[±]數(shù)字[.數(shù)字]e[±]數(shù)字

其中,[ ]中的內(nèi)容為可選項,根據(jù)具體情況可有可無,但其余部分必須有。如123e4、5e6、-7.0e-8等都是合法的指數(shù)形式浮點(diǎn)型常量;而e9、5e4.3和e都是不合法的表示形式。

3.字符型常量

字符型常量是單引號內(nèi)的字符,如'a','b'等。對于不可顯示的控制字符,可以在該字符前面加一個反斜杠“\”組成轉(zhuǎn)義字符。利用轉(zhuǎn)義字符可以完成一些特殊功能和輸出時的格式控制。常用轉(zhuǎn)義字符如表2-4所示。

表2-4 常用轉(zhuǎn)義字符表

4.字符串型常量

字符串型常量由雙引號""內(nèi)的字符組成,如"ABCD"、"$1234"等都是字符串常量。當(dāng)雙引號內(nèi)的字符個數(shù)為0時,稱為空串常量。需要注意的是,字符串常量首尾的雙引號是界限符,當(dāng)需要表示雙引號字符串時,可用雙引號轉(zhuǎn)義字符“\"”來表示。另外,C語言將字符串常量作為一個字符類型數(shù)組來處理,在存儲字符串常量時要在字符串的尾部加一個轉(zhuǎn)義字符\0作為該字符串常量的結(jié)束符。因此不要將字符常量與字符串常量混淆,如字符常量'a'與字符串常量"a"是不一樣的。

2.2.3 變量及其存儲模式

變量是一種在程序執(zhí)行過程中其值能不斷變化的量。使用一個變量之前,必須進(jìn)行定義,用一個標(biāo)識符作為變量名并指出它的數(shù)據(jù)類型和存儲模式,以便編譯系統(tǒng)為它分配相應(yīng)的存儲單元。在Cx51中對變量進(jìn)行定義的格式如下:

[存儲種類]數(shù)據(jù)類型 [存儲器類型]變量名表;

其中,“存儲種類”和“存儲器類型”是可選項。變量的存儲種類有四種:自動(auto)、外部(extern)、靜態(tài)(static)和寄存器(register)。定義一個變量時如果省略存儲種類選項,則該變量將為自動(auto)變量。定義一個變量時除了需要說明其數(shù)據(jù)類型之外,Keil Cx51編譯器還允許說明變量的存儲器類型。Keil Cx51編譯器完全支持8051系列單片機(jī)的硬件結(jié)構(gòu)和存儲器組織,對于每個變量可以準(zhǔn)確地賦予其存儲器類型,使之能夠在單片機(jī)系統(tǒng)內(nèi)準(zhǔn)確地定位。表2-5列出了Keil Cx51編譯器所能識別的存儲器類型。

表2-5 Keil Cx51編譯器所能識別的存儲器類型

定義變量時如果省略“存儲器類型”選項,則按編譯時使用的存儲器模式SMALL、COMPACT或LARGE來規(guī)定默認(rèn)存儲器類型,確定變量的存儲器空間,函數(shù)中不能采用寄存器傳遞的參數(shù)變量和過程變量也保存在默認(rèn)的存儲器空間。Keil Cx51編譯器的三種存儲器模式(默認(rèn)的存儲器類型)對變量的影響如下。

1.SMALL

變量被定義在8051單片機(jī)的片內(nèi)數(shù)據(jù)存儲器中,對這種變量的訪問速度最快。另外,所有的對象,包括堆棧,都必須位于片內(nèi)數(shù)據(jù)存儲器中,而堆棧的長度是很重要的,實際棧長取決于不同函數(shù)的嵌套深度。

2.COMPACT

變量被定義在分頁尋址的片外數(shù)據(jù)存儲器中,每一頁片外數(shù)據(jù)存儲器的長度為256字節(jié)。這時對變量的訪問是通過寄存器間接尋址(MOVX @Ri)進(jìn)行的,堆棧位于8051單片機(jī)片內(nèi)數(shù)據(jù)存儲器中。采用這種編譯模式時,變量的高8位地址由P2口確定,低8位地址由R0或R1的內(nèi)容決定。采用這種模式的同時,必須適當(dāng)改變啟動配置文件STARTUP.A51中的參數(shù):PDATASTART和PDATALEN;在用BL51進(jìn)行連接時還必須采用連接控制命令“PDATA”對P2口地址進(jìn)行定位,這樣才能確保P2口為所需要的高8位地址。

3.LARGE

變量被定義在片外數(shù)據(jù)存儲器中(最大可達(dá)64KB),使用數(shù)據(jù)指針DPTR來間接訪問變量(MOVX @DPTR)。這種訪問數(shù)據(jù)的方法效率是不高的,尤其是對于2個以上字節(jié)的變量,用這種方法相當(dāng)影響程序的代碼長度。

需要特別指出的是,變量的存儲種類與存儲器類型是完全無關(guān)的。例如:

    static unsigned char data x;    /* 在片內(nèi)數(shù)據(jù)存儲器中定義一個靜態(tài)無符
                                        號字符型變量x */
     int y;                         /* 定義一個自動整型變量y,它的存儲器類型由
                                        編譯模式確定 */

8051系列單片機(jī)具有多種內(nèi)部寄存器,其中一些是特殊功能寄存器,如定時器方式控制寄存器TMOD、中斷允許控制寄存器IE等。為了能夠直接訪問這些特殊功能寄存器,Keil Cx51編譯器擴(kuò)充了關(guān)鍵字sfr和sfr16,利用這種擴(kuò)充關(guān)鍵字可以在C語言源程序中直接對8051單片機(jī)的特殊功能寄存器進(jìn)行定義。定義方法如下:

sfr特殊功能寄存器名=地址常數(shù);

例如:

sfr P0 = 0x80;    /* 定義I/O口P0,其地址為0x80 */

這里需要注意的是,在關(guān)鍵字sfr后面必須跟一個標(biāo)識符作為寄存器名,名字可任意選取,但應(yīng)符合一般習(xí)慣。等號后面必須是常數(shù),不允許有帶運(yùn)算符的表達(dá)式,而且該常數(shù)必須在特殊功能寄存器的地址范圍之內(nèi)(0x80~0xFF)。在新一代的8051單片機(jī)中,特殊功能寄存器經(jīng)常組合成16位來使用。采用關(guān)鍵字sfr16可以定義這種16位的特殊功能寄存器。例如,對于8052單片機(jī)的定時器T2,可采用如下的方法來定義:

sfr16 T2 = 0xCC;   /* 定義TIMER2,其地址為T2L=0xCC,T2H=0xCD */

這里T2為特殊功能寄存器名,等號后面是它的低字節(jié)地址,其高字節(jié)地址必須在物理上直接位于低字節(jié)之后。這種定義方法適用于所有新一代的8051單片機(jī)中新增加的特殊功能寄存器。

在8051單片機(jī)應(yīng)用系統(tǒng)中經(jīng)常需要訪問特殊功能寄存器中的某些位,Keil Cx51編譯器為此提供了一個擴(kuò)充關(guān)鍵字sbit,利用它定義可位尋址對象。定義方法有如下三種。

(1)sbit位變量名=位地址

這種方法將位的絕對地址賦給位變量,位地址必須位于0x80~0xFF之間。例如:

sbit OV = 0xD2;
sbit CY = 0xD7;

(2)sbit位變量名=特殊功能寄存器名^位位置

當(dāng)可尋址位位于特殊功能寄存器中時可采用這種方法,“位位置”是一個0~7之間的常數(shù)。例如:

sfr PSW = 0xD0;
sbit OV = PSW^2;
sbit CY = PSW^7;

(3)sbit位變量名=字節(jié)地址^位位置

這種方法以一個常數(shù)(字節(jié)地址)作為基地址,該常數(shù)必須在0x80H~0xFF之間。“位位置”是一個0~7之間的常數(shù)。例如:

sbit OV = 0xD0^2;
sbit CY = 0xD0^7;

當(dāng)位對象位于8051單片機(jī)片內(nèi)存儲器中可位尋址區(qū)時稱之為“可位尋址對象”。Keil Cx51編譯器提供了一個bdata存儲器類型,允許將具有bdata類型的對象放入8051單片機(jī)片內(nèi)可位尋址區(qū)。例如:

int bdata ibase;            /*在位尋址區(qū)定義一個整型變量ibase  */
char bdata bary[4];         /*在位尋址區(qū)定義一個數(shù)組array[4]  */

使用關(guān)鍵字sbit可以獨(dú)立訪問可位尋址對象中的某一位。例如:

sbit mybit0=ibase^0;
sbit mybit15=ibase^15;
sbit Ary07=bary[0]^7;
sbit Ary37=bary[3]^7;

采用這種方法定義可位尋址變量時要求基址對象的存儲器類型為bdata,操作符“^”后面“位位置”的最大值取決于指定的基地址類型,對于char類型來說是0~7;對于int類型來說是0~15;對于long類型來說是0~31。

需要注意的是,sbit是一個獨(dú)立的關(guān)鍵字,不要將它與關(guān)鍵字bit相混淆。關(guān)鍵字bit是Keil Cx51編譯器的一種擴(kuò)充數(shù)據(jù)類型,用來定義一個普通位變量,它的值是二進(jìn)制數(shù)的0或1。一個函數(shù)中可以包含bit類型的參數(shù),函數(shù)的返回值也可為bit類型。例如:

static bit direction_bit        /* 定義一個靜態(tài)位變量direction_bit */
extern bit lock_prt_port        /* 定義一個外部位變量lock_prt_port */
bit bfunc(bit b0,bit b1);       /* 定義一個返回位型值的函數(shù)bfunc,函
    {...                             數(shù)中包含有兩個位型參數(shù)b0 和b1 */
    return(b1)                  /* 返回一個位型值b1 */
}

如果在函數(shù)中禁止使用中斷(#pragma disable)或者函數(shù)中包含有明確的寄存器組切換(using n),則該函數(shù)不能返回位型值,否則在編譯時會產(chǎn)生編譯錯誤。另外,不能定義位指針,也不能定義位數(shù)組。

上面介紹了變量及其定義方法,這在編寫C語言程序時是十分重要的。從變量的作用范圍來看,還有全局變量和局部變量之分。全局變量是指在程序開始處或各個功能函數(shù)的外面定義的變量,在程序開始處定義的全局變量對于整個程序都有效,可供程序中所有函數(shù)共同使用;而在各功能函數(shù)外面定義的全局變量只對從定義處開始往后的各個函數(shù)有效,只有從定義處往后的那些功能函數(shù)才可以使用該變量,定義處前面的函數(shù)則不能使用。

局部變量是指在函數(shù)內(nèi)部或以花括號{ }圍起來的功能塊內(nèi)部所定義的變量,局部變量只在定義它的函數(shù)或功能塊以內(nèi)有效,在該函數(shù)或功能塊以外則不能使用。局部變量可以與全局變量同名,但在這種情況下局部變量的優(yōu)先級較高,而同名的全局變量在該功能塊內(nèi)被暫時屏蔽。

從變量的存在時間來看又可分為靜態(tài)存儲變量和動態(tài)存儲變量。

靜態(tài)存儲變量是指該變量在程序運(yùn)行期間其存儲空間固定不變;動態(tài)存儲變量是指該變量的存儲空間不確定,在程序運(yùn)行期間根據(jù)需要動態(tài)地為該變量分配存儲空間。一般來說,全局變量為靜態(tài)存儲變量,局部變量為動態(tài)存儲變量。

在進(jìn)行程序設(shè)計的時候經(jīng)常需要給一些變量賦以初值,C語言允許在定義變量的同時給變量賦初值。下面是一些變量定義的例子。

char data var1;                          /* 在data區(qū)定義字符型變量var1 */
int  idata var2;                         /* 在idata區(qū)定義整型變量var2 */
int a=5;                                 /* 定義變量a,同時賦以初值5,變量a位于
                                            由編譯模式確定的默認(rèn)存儲區(qū) */
char code text[ ]="ENTER PARAMETER:";    /* 在code區(qū)定義字符串?dāng)?shù)組 */
unsigned char xdata vecter [10][4][4];   /* 在xdata區(qū)定義無符號字符型三維數(shù)組
                                            變量vecter[10][4][4] */
static unsigned long xdata array [100];  /* 在xdata區(qū)定義靜態(tài)無符號長整型數(shù)組
                                            變量array[100]*/
extern float idata x,y,z;                /* 在idata區(qū)定義外部浮點(diǎn)型變量x,y,z*/
char xdata * px;                         /* 在xdata區(qū)定義一個指向?qū)ο箢愋蜑?
                                            char的指針px,指針px自身在默認(rèn)存
                                            儲區(qū)(由編譯模式確定),長度為2字節(jié)
                                           (0~0xFFFF)*/
char xdata * data pdx;                   /* 除了指針明確定位于內(nèi)部數(shù)據(jù)存儲器區(qū)
                                            (data)之外,與上例完全相同,由于指定
                                            了存儲器類型,所以與編譯模式無關(guān) */
extern bit data lock_prt_port;           /* 在data區(qū)定義一個外部位變量 */
char bdata flags;                        /* 在bdata區(qū)定義字符型變量 */
sbit flag0=flags^0;                      /* 在bdata區(qū)定義可位尋址變量 */
sfr P0=ox80;                             /* 定義特殊功能寄存器P0 */
sfr16 T2=0xCC;                           /* 定義特殊功能寄存器T2 */

2.2.4 用typedef重新定義數(shù)據(jù)類型

在C語言程序中除了可以采用上面所介紹的數(shù)據(jù)類型之外,用戶還可以根據(jù)自己的需要對數(shù)據(jù)類型重新定義。重新定義時需用到關(guān)鍵字typedef,定義方法如下:

typedef  已有的數(shù)據(jù)類型  新的數(shù)據(jù)類型名;

其中“已有的數(shù)據(jù)類型”是指上面所介紹的C語言中所有的數(shù)據(jù)類型,包括結(jié)構(gòu)、指針和數(shù)組等,“新的數(shù)據(jù)類型名”可按用戶自己的習(xí)慣或根據(jù)任務(wù)需要決定。關(guān)鍵字typedef的作用只是將C語言中已有的數(shù)據(jù)類型作了置換,因此可用置換后的新數(shù)據(jù)類型名來進(jìn)行變量的定義。例如:

typedef int word;       /* 定義word為新的整型數(shù)據(jù)類型名 */
word i,j;               /* 將I,j定義為int型變量 */

在這個例子中,先用關(guān)鍵字typedef將word定義為新的整型數(shù)據(jù)類型,定義的過程實際上是用word置換了int,因此下面就可以直接用word對變量i,j進(jìn)行定義,而此時word等效于int,所以i,j被定義成整型變量。例如:

typedef int NUM[100]; /* 將NUM定義為整型數(shù)組類型 */
NUM  n;                 /* 將n定義為整型數(shù)組變量 */
typedef char * POINTER; /* 將POINTER定義為字符指針類型 */
POINTER point;          /* 將point定義為字符指針變量 */

用typedef還可以定義結(jié)構(gòu)類型:

typedef struct          /* 定義結(jié)構(gòu)體 */
    { int month;
      int day;
      int year;
    } DATE;

這里DATE為一個新的數(shù)據(jù)類型(結(jié)構(gòu)類型)名,可以直接用它來定義變量:

DATE birthday;          /* 定義birthday為結(jié)構(gòu)類型變量 */
DATE * point;           /* 定義指向這個結(jié)構(gòu)類型數(shù)據(jù)的指針 */

關(guān)于結(jié)構(gòu)類型數(shù)據(jù)在本章后面還要詳細(xì)討論。一般而言,用typedef定義的新數(shù)據(jù)類型用大寫字母表示,以便與C語言中原有的數(shù)據(jù)類型相區(qū)別。另外還要注意,用typedef可以定義各種新的數(shù)據(jù)類型名,但不能直接用來定義變量。typedef只是對已有的數(shù)據(jù)類型作了一個名字上的置換,并沒有創(chuàng)造出一個新的數(shù)據(jù)類型,例如前面例子中的word,它只是int類型的一個新名字而已。

采用typedef來重新定義數(shù)據(jù)類型有利于程序的移植,同時還可以簡化較長的數(shù)據(jù)類型定義(如結(jié)構(gòu)數(shù)據(jù)類型等)。在采用多模塊程序設(shè)計時,如果不同的模塊程序源文件中用到同一類型的數(shù)據(jù)時(尤其是像數(shù)組、指針、結(jié)構(gòu)、聯(lián)合等復(fù)雜數(shù)據(jù)類型),經(jīng)常用typedef將這些數(shù)據(jù)重新定義并放到一個單獨(dú)的文件中,需要時再用預(yù)處理命令#include將它們包含進(jìn)來。

2.2.5 運(yùn)算符與表達(dá)式

C語言對數(shù)據(jù)有很強(qiáng)的表達(dá)能力,具有十分豐富的運(yùn)算符。運(yùn)算符就是完成某種特定運(yùn)算的符號,表達(dá)式則是由運(yùn)算符及運(yùn)算對象所組成的具有特定含義的一個式子。C語言是一種表達(dá)式語言,在任意一個表達(dá)式的后面加一個分號“;”就構(gòu)成了一個表達(dá)式語句。由運(yùn)算符和表達(dá)式可以組成C語言程序的各種語句。

運(yùn)算符按其在表達(dá)式中所起的作用,可分為賦值運(yùn)算符、算術(shù)運(yùn)算符、增量與減量運(yùn)算符、關(guān)系運(yùn)算符、邏輯運(yùn)算符、位運(yùn)算符、復(fù)合賦值運(yùn)算符、逗號運(yùn)算符、條件運(yùn)算符、指針和地址運(yùn)算符、強(qiáng)制類型轉(zhuǎn)換運(yùn)算符和sizeof運(yùn)算符等。運(yùn)算符按其在表達(dá)式中與運(yùn)算對象的關(guān)系,又可分為單目運(yùn)算符、雙目運(yùn)算符和三目運(yùn)算符等。單目運(yùn)算符只需要有一個運(yùn)算對象,雙目運(yùn)算符要求有兩個運(yùn)算對象,三目運(yùn)算符要求有三個運(yùn)算對象。掌握各種運(yùn)算符的意義和使用規(guī)則,對于編寫正確的C語言程序是十分重要的。

1.賦值運(yùn)算符

在C語言中,符號“=”是一個特殊的運(yùn)算符,稱之為賦值運(yùn)算符。賦值運(yùn)算符的作用是將一個數(shù)據(jù)的值賦給一個變量,利用賦值運(yùn)算符將一個變量與一個表達(dá)式連接起來的式子稱為賦值表達(dá)式,在賦值表達(dá)式的后面加一個分號“;”便構(gòu)成了賦值語句。賦值語句的格式如下:

變量 = 表達(dá)式;

該語句的意義是先計算出右邊表達(dá)式的值,然后將該值賦給左邊的變量。上式中的“表達(dá)式”還可以是另一個賦值表達(dá)式,即C語言允許進(jìn)行多重賦值。例如:

x=9;         /* 將常數(shù) 9 賦給變量x */
x=y=8;       /* 將常數(shù) 8 同時賦給變量x和y */

這些都是合法的賦值語句。在使用賦值運(yùn)算符“=”時應(yīng)注意不要與關(guān)系運(yùn)算符“= =”相混淆,運(yùn)算符“= =”用來進(jìn)行相等關(guān)系運(yùn)算。

2.算術(shù)運(yùn)算符

C語言中的算術(shù)運(yùn)算符有:

+ 加或取正值運(yùn)算符

- 減或取負(fù)值運(yùn)算符

* 乘運(yùn)算符

/ 除運(yùn)算符

% 取余運(yùn)算符

上面這些運(yùn)算符中加、減、乘、除為雙目運(yùn)算符,它們要求有兩個運(yùn)算對象。對于加、減和乘法符合一般的算術(shù)運(yùn)算規(guī)則。除法運(yùn)算有所不同,如果是兩個整數(shù)相除,其結(jié)果為整數(shù),舍去小數(shù)部分,例如:5/3的結(jié)果為1,5/10的結(jié)果為0。如果是兩個浮點(diǎn)數(shù)相除,其結(jié)果為浮點(diǎn)數(shù),例如:5.0/10.0的結(jié)果為0.5。取余運(yùn)算要求兩個運(yùn)算對象均為整型數(shù)據(jù),例如:7%4的結(jié)果為3。取正值和取負(fù)值為單目運(yùn)算符,它們的運(yùn)算對象只有一個,分別是取運(yùn)算對象的正值和負(fù)值。

用算術(shù)運(yùn)算符將運(yùn)算對象連接起來的式子即為算術(shù)表達(dá)式。算術(shù)運(yùn)算的一般形式為:

表達(dá)式1  算術(shù)運(yùn)算符  表達(dá)式2

例如:x+y/(a-b),(a+b)*(x-y)都是合法的算術(shù)表達(dá)式。C語言中規(guī)定了運(yùn)算符的優(yōu)先級和結(jié)合性。在求一個表達(dá)式的值時,要按運(yùn)算符的優(yōu)先級別進(jìn)行。算術(shù)運(yùn)算符中取負(fù)值(-)的優(yōu)先級最高,其次是乘法(*)、除法(/)和取余(%)運(yùn)算符,加法(+)和減法(-)運(yùn)算符的優(yōu)先級最低。

需要時可在算術(shù)表達(dá)式中采用圓括號來改變運(yùn)算符的優(yōu)先級,例如在計算表達(dá)式x+y/(a-b)的值時,首先計算(a-b),然后再計算y/(a-b),最后計算x+y/(a-b)。如果在一個表達(dá)式中各個運(yùn)算符的優(yōu)先級別相同,則計算時按規(guī)定的結(jié)合方向進(jìn)行。例如計算表達(dá)式x+y-z的值,由于+和-優(yōu)先級別相同,計算時按“從左至右”的結(jié)合方向,先計算x+y,再計算(x+y)-z。這種“從左至右”的結(jié)合方向稱為“左結(jié)合性”,此外還有“右結(jié)合性”。

3.增量和減量運(yùn)算符

C語言中除了基本的加、減、乘、除運(yùn)算符之外,還提供一種特殊的運(yùn)算符:

++ 增量運(yùn)算符

-- 減量運(yùn)算符

增量和減量是C語言中特有的一種運(yùn)算符,它們的作用分別是對運(yùn)算對象作加1和減1運(yùn)算。例如:++i,i++,--j,j--等。

看起來++i和i++的作用都是使變量i的值加1,但是由于運(yùn)算符++所處的位置不同,使變量i加1的運(yùn)算過程也不同。++i(或--i)是先執(zhí)行i+1(或i-1)操作,再使用i的值,而i++(或i--)則是先使用i的值,再執(zhí)行i+1(或i-1)操作。

增量運(yùn)算符++和減量運(yùn)算符--只能用于變量,不能用于常數(shù)或表達(dá)式。

例2.1:使用增量“++”和減量“- -”運(yùn)算符的例子。

    #include <stdio.h>
    main(){
        int  x,y,z;
        x = y = 8;  z = ++x;
        printf("\n %d %d %d",y,z,x);
        x = y = 8;  z = x++;
        printf("\n %d %d %d",y,z,x);
        x = y = 8;  z = --x;
        printf("\n %d %d %d",y,z,x);
        x = y = 8;  z = x--;
        printf("\n %d %d %d",y,z,x);
        printf("\n");
        while(1);
    }

程序執(zhí)行結(jié)果:

    8 9 9
    8 8 9
    8 7 7
    8 8 7

在這個程序例子中使用了Keil Cx51編譯器提供的輸出庫函數(shù)printf,在C語言程序中凡是使用了庫函數(shù)的,都必須在程序開始處將該庫函數(shù)的預(yù)定義文件包含進(jìn)來,才能使程序得到正確的編譯和執(zhí)行。本程序在開始處使用了預(yù)處理命令#include將聲明庫函數(shù)printf原型的頭文件stdio.h包含到程序中去。另外,為了使庫函數(shù)printf能夠在μVision2仿真調(diào)試狀態(tài)下正確工作,應(yīng)在C語言源程序中增加對8051單片機(jī)串行口初始化的語句,或者將C語言源程序與修改后(加入了8051單片機(jī)串口初始化指令)的啟動程序STARTUP.A51連接在一起。關(guān)于輸入輸出庫函數(shù)的詳細(xì)介紹請參見本書第9章。

4.關(guān)系運(yùn)算符

C語言中有6種關(guān)系運(yùn)算符:

> 大于

< 小于

>= 大于等于

<= 小于等于

= = 等于

!= 不等于

前4種關(guān)系運(yùn)算符具有相同的優(yōu)先級,后兩種關(guān)系運(yùn)算符也具有相同的優(yōu)先級;但前4種的優(yōu)先級高于后2種。用關(guān)系運(yùn)算符將兩個表達(dá)式連接起來即成為關(guān)系表達(dá)式。關(guān)系表達(dá)式的一般形式為:

表達(dá)式1  關(guān)系運(yùn)算符  表達(dá)式2

例如:x>y,x+y>z,(x=3)>(y=4)都是合法的關(guān)系表達(dá)式。

關(guān)系運(yùn)算符通常用來判別某個條件是否滿足,關(guān)系運(yùn)算的結(jié)果只有0和1兩種值。當(dāng)所指定的條件滿足時結(jié)果為1,條件不滿足時結(jié)果為0。

例2.2:使用關(guān)系運(yùn)算符的例子。

#include <stdio.h>
main() {
    int  x,y,z;
    printf("input data x,y ? \n");
    scanf("%d %d",&x,&y);
    printf("\n   x   y   x<y  x<=y x>y  x>=y x!=y x==y");
    printf("\n%5d%5d",x,y);
    z = x <  y; printf("%5d",z);
    z = x <= y; printf("%5d",z);
    z = x >  y; printf("%5d",z);
    z = x >= y; printf("%5d",z);
    z = x != y; printf("%5d",z);
    z = x == y; printf("%5d",z);
    printf("\n");
    while(1);
}

程序執(zhí)行結(jié)果(1):

input data x,y ?
5  3  回車
  x   y   x<y   x<=y   x>y   x>=y   x!=y   x==y
  5   3   0     0      1     1      1      0

程序執(zhí)行結(jié)果(2):

input data x,y ?
-5  -3 回車
  x   y   x<y   x<=y   x>y   x>=y   x!=y   x==y
  -5  -3  1     1      0     0      1      0

程序執(zhí)行結(jié)果(3):

input data x,y ?
4  4 回車
  x   y   x<y   x<=y   x>y   x>=y   x!=y   x==y
  4   4   0     1      0     1      0      1

在本例中使用了Keil Cx51編譯器提供的輸入庫函數(shù)scanf,與printf函數(shù)一樣,scanf也是通過8051單片機(jī)的串行口實現(xiàn)數(shù)據(jù)輸入的,它的使用方法與printf函數(shù)類似。

5.邏輯運(yùn)算符

C語言中有3種邏輯運(yùn)算符:

|| 邏輯或

&& 邏輯與

! 邏輯非

邏輯運(yùn)算符用來求某個條件式的邏輯值,用邏輯運(yùn)算符將關(guān)系表達(dá)式或邏輯量連接起來就是邏輯表達(dá)式。邏輯運(yùn)算的一般形式為:

邏輯與   條件式1 && 條件式2
邏輯或   條件式1 || 條件式2
邏輯非   ! 條件式

例如:x&&y,a||b,!z都是合法的邏輯表達(dá)式。

進(jìn)行邏輯與運(yùn)算時,首先對條件式1進(jìn)行判斷,如果結(jié)果為真(非0值),則繼續(xù)對條件式2進(jìn)行判斷,當(dāng)結(jié)果也為真時,表示邏輯運(yùn)算的結(jié)果為真(值為1);反之,如果條件式1的結(jié)果為假,則不再判斷條件式2,而直接給出邏輯運(yùn)算的結(jié)果為假(值為0)。

進(jìn)行邏輯或運(yùn)算時,只要兩個條件式中有一個為真,邏輯運(yùn)算的結(jié)果便為真(值為1),只有當(dāng)條件式1和條件式2均不成立時,邏輯運(yùn)算的結(jié)果才為假(值為0)。

進(jìn)行邏輯非運(yùn)算時,對條件式的邏輯值直接取反。

邏輯運(yùn)算符的優(yōu)先級為(由高至低):!(非)→&&(與)→||(或),即邏輯非的優(yōu)先級最高。

例2.3:使用邏輯運(yùn)算符的例子。

#include <stdio.h>
main() {
    int  x,y,z;
    printf("input data x,y ? \n");
    scanf("%d %d",&x,&y);
    printf("\n   x   y     !x     x||y   x&&y");
    printf("\n%5d%5d",x,y);
    z = !x;    printf("%8d",z);
    z = x || y; printf("%8d",z);
    z = x && y; printf("%8d",z);
    printf("\n");
    while(1);
}

程序執(zhí)行結(jié)果(1):

input data x,y ?
12  8  回車
x   y   !x   x||y   x&&y
12   8    0     1      1

程序執(zhí)行結(jié)果(2):

input data x,y ?
9  -3  回車
x   y   !x   x||y   x&&y
9   -3   0     1      1

程序執(zhí)行結(jié)果(3):

input data x,y ?
0  81  回車
x   y   !x   x||y   x&&y
0   81   1     1      0

程序執(zhí)行結(jié)果(4):

input data x,y ?
-23  0  回車
x   y   !x   x||y   x&&y
-23  0    0     1      0

程序執(zhí)行結(jié)果(5):

input data x,y ?
0   0  回車
x   y   !x   x||y   x&&y
0   0    1     0      0

6.位運(yùn)算符

能對運(yùn)算對象進(jìn)行按位操作是C語言的一大特點(diǎn),正是由于這一特點(diǎn)使C語言具有了匯編語言的一些功能,從而使之能對計算機(jī)的硬件直接進(jìn)行操作。C語言中共有6種位運(yùn)算符:

~ 按位取反

<< 左移

>> 右移

& 按位與

^ 按位異或

| 按位或

位運(yùn)算符的作用是按位對變量進(jìn)行運(yùn)算,并不改變參與運(yùn)算的變量的值。若希望按位改變運(yùn)算變量的值,則應(yīng)利用相應(yīng)的賦值運(yùn)算。另外位運(yùn)算符不能用來對浮點(diǎn)型數(shù)據(jù)進(jìn)行操作。位運(yùn)算符的優(yōu)先級從高到低依次是:按位取反(~)→左移(<<)和右移(>>)→按位與(&)→按位異或(^)→按位或(|)。位運(yùn)算的一般形式如下:

變量1  位運(yùn)算符  變量2

表2-6列出了按位取反、按位與、按位或和按位異或的邏輯真值。

表2-6 按位取反、按位與、按位或和按位異或的邏輯真值

例2.4:位邏輯運(yùn)算。

#include <stdio.h>
main() {
    unsigned int  x = 0x57db,y = 0xb0f3;
    printf("\n   x    y   x&y   x^y   x|y   ~x");
    printf("\n%6x%6x%6x%6x%6x%6x",x,y,x&y,x^y,x|y,~x);
    printf("\n");
    while(1);
}
    x      y    x&y    x^y    x|y    ~x
    57db  b0f3   10d3   e728   f7fb   a824

程序執(zhí)行結(jié)果:

位運(yùn)算符中的移位操作比較復(fù)雜。左移(<<)運(yùn)算符是用來將變量1的二進(jìn)制位值向左移動由變量2所指定的位數(shù)。例如:a=0x8f(即二進(jìn)制數(shù)10001111),進(jìn)行左移運(yùn)算a<<2,就是將a的全部二進(jìn)制位值一起向左移動2位,其左端移出的位值被丟棄,并在其右端補(bǔ)以相應(yīng)位數(shù)的“0”。因此,移位的結(jié)果是a=0x3c(即二進(jìn)制數(shù)(00111100)。

右移(>>)運(yùn)算符是用來將變量1的二進(jìn)制位值向右移動由變量2指定的位數(shù)。進(jìn)行右移運(yùn)算時,如果變量1屬于無符號類型數(shù)據(jù),則總是在其左端補(bǔ)“0”;如果變量1屬于有符號類型數(shù)據(jù),則在其左端補(bǔ)入原來數(shù)據(jù)的符號位(即保持原來的符號不變),其右端的移出位被丟棄。對于a= 0x8f,如果a是無符號數(shù),則執(zhí)行a>>2之后結(jié)果為a=0x23(即二進(jìn)制數(shù)00100011);如果a是有符號數(shù),則執(zhí)行a>>2之后結(jié)果為a=0xe3(即二進(jìn)制數(shù)11100011)。

例2.5:移位運(yùn)算。

    #include <stdio.h>
    main() {
    int a,b;
    unsigned int  x,y;
    a = b = 0xaa55;  x = y = 0xaa55;
    printf("\n a=%4x  b=%4x  x=%4x  y=%4x",a,b,x,y);
        a = a << 1;  b = b >> 1;
        x = x << 1;  y = y >> 1;
        printf("\n a=%4x  b=%4x  x=%4x  y=%4x",a,b,x,y);
        printf("\n");
        while(1);
    }

程序執(zhí)行結(jié)果:

a=aa55   b=aa55   x=aa55   y=aa55
a=54aa   b=d52a   x=54aa   y=552a

7.復(fù)合賦值運(yùn)算符

在賦值運(yùn)算符“=”的前面加上其他運(yùn)算符,就構(gòu)成了所謂復(fù)合賦值運(yùn)算符:

+= 加法賦值

-= 減法賦值

*= 乘法賦值

/= 除法賦值

%= 取模賦值

<<= 左移位賦值

>>= 右移位賦值

&= 邏輯與賦值

|= 邏輯或賦值

^= 邏輯異或賦值

~= 邏輯非賦值

復(fù)合賦值運(yùn)算首先對變量進(jìn)行某種運(yùn)算,然后將運(yùn)算的結(jié)果再賦給該變量。復(fù)合運(yùn)算的一般形式為:

變量  復(fù)合賦值運(yùn)算符  表達(dá)式

例如:a+=3等價于a=a+3;x*=y+8等價于x=x*(y+8)。凡是二目運(yùn)算符,都可以和賦值運(yùn)算符一起組合成復(fù)合賦值運(yùn)算符。采用復(fù)合賦值運(yùn)算符,可以使程序簡化,同時還可以提高程序的編譯效率。

例2.6:利用復(fù)合賦值運(yùn)算符實現(xiàn)算術(shù)運(yùn)算。

#include <stdio.h>
main() {
    int a,b,c,d,x,y,z;
    x = 634; y = 19; z = 28;
    a = 3 * (b = x/(y-4)) - z/2;
    printf("\n%10d%10d",a,b);
    a = 100; b = 45; c = -19; d = 94; x = -2; y = 5;
    a += 6;
    b -= x;
    c *= 10;
    d /= x+y;
    z %= 8;
    printf("\n%10d%10d%10d%10d%10d",a,b,c,d,z);
    printf("\n");
    while(1);
}

程序執(zhí)行結(jié)果:

112   42
106   47   -190   31   4

例2.7:利用復(fù)合賦值運(yùn)算符實現(xiàn)位邏輯運(yùn)算。

#include <stdio.h>
main() {
    int  x,y;
    x = 2; y = 3;
    x <<= 2; printf("\n%3d",x);
    x >>= 1; printf("\n%3d",x);
    x <<= y; printf("\n%3d",x);
    x = 2;
    x &= y;  printf("\n%3d",x);
    x |= y;  printf("\n%3d",x);
    x ^= y;  printf("\n%3d",x);
    printf("\n");
    while(1);
}

程序執(zhí)行結(jié)果:

8
4
32
2
3
0

8.逗號運(yùn)算符

在C語言中逗號“,”是一個特殊的運(yùn)算符,可以用它將兩個(或多個)表達(dá)式連接起來,稱為逗號表達(dá)式。逗號表達(dá)式的一般形式為:

表達(dá)式1,表達(dá)式2,…,表達(dá)式n

程序運(yùn)行時對于逗號表達(dá)式的處理,是從左至右依次計算出各個表達(dá)式的值,而整個逗號表達(dá)式的值是最右邊表達(dá)式(即表達(dá)式n)的值。

例2.8:逗號運(yùn)算符的使用。

#include <stdio.h>
main() {
    int  a,b,c,w,x,y,z;
    w = ( x=5,y=-11,z=43,3);
    printf("\n%d %d %d %d",w,x,y,z);
    a = 3 * (b = w + x,c = y * ( z -10)) -6;
    printf("\n%d %d %d",a,b,c);
    printf("\n");
    while(1);
}

程序執(zhí)行結(jié)果:

3  5  -11  43
-1095  8  -363

在許多情況下,使用逗號表達(dá)式的目的只是為了分別得到各個表達(dá)式的值,而并不一定要得到和使用整個逗號表達(dá)式的值。另外還要注意,并不是在程序的任何地方出現(xiàn)的逗號,都可以認(rèn)為是逗號運(yùn)算符。例如函數(shù)中的參數(shù)也是用逗號來間隔的,上例中庫輸出函數(shù)printf("\n%d %d %d",a,b,c)中的“a,b,c”是函數(shù)的三個參數(shù),而不是一個逗號表達(dá)式。

9.條件運(yùn)算符

條件運(yùn)算符“?:”是C語言中唯一的一個三目運(yùn)算符,它要求有三個運(yùn)算對象,用它可以將三個表達(dá)式連接構(gòu)成一個條件表達(dá)式。條件表達(dá)式的一般形式如下:

邏輯表達(dá)式  ?  表達(dá)式1:表達(dá)式2

其功能是首先計算邏輯表達(dá)式,當(dāng)值為真(非0值)時,將表達(dá)式1的值作為整個條件表達(dá)式的值;當(dāng)邏輯表達(dá)式的值為假(0值)時,將表達(dá)式2的值作為整個條件表達(dá)式的值。例如:條件表達(dá)式max=(a>b) ? a:b的執(zhí)行結(jié)果是將a和b中較大者賦值給變量max。另外,條件表達(dá)式中邏輯表達(dá)式的類型可以與表達(dá)式1和表達(dá)式2的類型不一樣。

10.指針和地址運(yùn)算符

指針是C語言中的一個十分重要的概念,在C語言的數(shù)據(jù)類型中專門有一種指針類型。變量的指針就是該變量的地址,還可以定義一個指向某個變量的指針變量。為了表示指針變量和它所指向的變量地址之間的關(guān)系,C語言提供了兩個專門的運(yùn)算符:

* 取內(nèi)容

& 取地址

取內(nèi)容和取地址運(yùn)算的一般形式分別為:

變量 = * 指針變量
指針變量 = & 目標(biāo)變量

取內(nèi)容運(yùn)算的含義是將指針變量所指向的目標(biāo)變量的值賦給左邊的變量;取地址運(yùn)算的含義是將目標(biāo)變量的地址賦給左邊的變量。需要注意的是,指針變量中只能存放地址(即指針型數(shù)據(jù)),不要將一個非指針類型的數(shù)據(jù)賦值給一個指針變量。

例2.9:指針及地址運(yùn)算符的使用。

#include <stdio.h>
main() {
    int i;
    int *int_ptr;
    int_ptr = &i;
    *int_ptr = 5;
    printf("\n i = %d",i);
    while(1);
}

程序執(zhí)行結(jié)果:

i=5

11.強(qiáng)制類型轉(zhuǎn)換運(yùn)算符

C語言中的圓括號“()”也可作為一種運(yùn)算符使用,這就是強(qiáng)制類型轉(zhuǎn)換運(yùn)算符,它的作用是將表達(dá)式或變量的類型強(qiáng)制轉(zhuǎn)換成為所指定的類型。在C語言程序中進(jìn)行算術(shù)運(yùn)算時,需要注意數(shù)據(jù)類型的轉(zhuǎn)換。有兩種數(shù)據(jù)類型轉(zhuǎn)換方式,即隱式轉(zhuǎn)換和顯式轉(zhuǎn)換。隱式轉(zhuǎn)換是在對程序進(jìn)行編譯時由編譯器自動處理的。隱式轉(zhuǎn)換遵循以下規(guī)則。

① 所有char型的操作數(shù)轉(zhuǎn)換成int型。

② 用運(yùn)算符連接的兩個操作數(shù)如果具有不同的數(shù)據(jù)類型,按以下次序進(jìn)行轉(zhuǎn)換:如果一個操作數(shù)是float類型,則另一個操作數(shù)也轉(zhuǎn)換成float類型;如果一個操作數(shù)是long類型,則另一個操作數(shù)也轉(zhuǎn)換成long類型;如果一個操作數(shù)是unsigned類型,則另一個操作數(shù)也轉(zhuǎn)換成unsigned類型。

③ 在對變量賦值時發(fā)生的隱式轉(zhuǎn)換,將賦值號“=”右邊的表達(dá)式類型轉(zhuǎn)換成賦值號左邊變量的類型。例如,把整型數(shù)賦值給字符型變量,則整型數(shù)的高8位將喪失;把浮點(diǎn)數(shù)賦值給整型變量,則小數(shù)部分將喪失。在C語言中只有基本數(shù)據(jù)類型(即char、int、long和float)可以進(jìn)行隱式轉(zhuǎn)換。其余的數(shù)據(jù)類型不能進(jìn)行隱式轉(zhuǎn)換,例如,我們不能把一個整型數(shù)利用隱式轉(zhuǎn)換賦值給一個指針變量,在這種情況下就必須利用強(qiáng)制類型轉(zhuǎn)換運(yùn)算符來進(jìn)行顯式轉(zhuǎn)換。強(qiáng)制類型轉(zhuǎn)換運(yùn)算符的一般使用形式為:

(類型)=表達(dá)式

顯式類型轉(zhuǎn)換在給指針變量賦值時特別有用。例如,預(yù)先在8051單片機(jī)的片外數(shù)據(jù)存儲器(xdata)中定義了一個字符型指針變量px,如果想給這個指針變量賦一初值0xB000,可以寫成:px=(char xdata *)0xB000;這種方法特別適合于用標(biāo)識符來存取絕對地址。

例2.10:強(qiáng)制類型轉(zhuǎn)換運(yùn)算符的使用。

#include <stdio.h>
main() {
    char xdata * px;
    char q;
    int   x = 0xf32a;
    long  y = 0x901af364;
    float z = 3.14159;
    px=(char xdata *)0xB000;
    * px='A';
    q=*((char xdata *)0xB000);
    printf("\n%bx  %x  %d  %c",(char)x,(int)y,(int)z,q) ;
    while(1);
}

程序執(zhí)行結(jié)果:

2a  f364  3  A

12.sizeof運(yùn)算符

C語言中提供了一種用于求取數(shù)據(jù)類型、變量以及表達(dá)式的字節(jié)數(shù)的運(yùn)算符:sizeof,該運(yùn)算符的一般使用形式為:

sizeof(表達(dá)式)或sizeof(數(shù)據(jù)類型)

應(yīng)該注意的是,sizeof是一種特殊的運(yùn)算符,不要錯誤地認(rèn)為它是一個函數(shù)。實際上,字節(jié)數(shù)的計算在程序編譯時就完成了,而不是在程序執(zhí)行的過程中才計算出來的。

例2.11:sizeof運(yùn)算符的使用。

#include <stdio.h>
main() {
    printf("\n    char: %bd byte",sizeof(char));
    printf("\n     int: %bd bytes",sizeof(int));
    printf("\n    long: %bd bytes",sizeof(long));
    printf("\n   float: %bd bytes",sizeof(float));
    while(1);
}

程序執(zhí)行結(jié)果:

char: 1 byte
int:  2 bytes
long: 4 bytes
float:4 bytes

前面對C語言中的各種運(yùn)算符分別作了介紹,此外還有三個運(yùn)算符:數(shù)組下標(biāo)運(yùn)算符“[ ]”、存取結(jié)構(gòu)或聯(lián)合中變量的運(yùn)算符“->”或“.”,它們將在第5章予以介紹。表2-7給出了這些運(yùn)算符在使用過程中的優(yōu)先級和結(jié)合性。

表2-7 運(yùn)算符的優(yōu)先級和結(jié)合性

2.3 Cx51程序的基本語句

2.3.1 表達(dá)式語句

C語言是一種結(jié)構(gòu)化的程序設(shè)計語言,它提供了十分豐富的程序控制語句。表達(dá)式語句是最基本的一種語句。在表達(dá)式的后邊加一個分號“;”就構(gòu)成了表達(dá)式語句。下面的語句都是合法的表達(dá)式語句:

a=++b*9;
x=8; y=7;
z=(x+y)/a;
++i;

表達(dá)式語句也可以僅由一個分號“;”組成,這種語句稱為空語句。空語句是表達(dá)式語句的一個特例。空語句在程序設(shè)計中有時是很有用的,當(dāng)程序在語法上需要有一個語句,但在語義上并不要求有具體的動作時,便可以采用空語句。空語句通常有以下兩種用法。

① 在程序中為有關(guān)語句提供標(biāo)號,用以標(biāo)記程序執(zhí)行的位置。例如,采用下面的語句可以構(gòu)成一個循環(huán)。

repeat:;
        ..
      goto repeat ;

② 在用while語句構(gòu)成的循環(huán)語句后面加一個分號,形成一個不執(zhí)行其他操作的空循環(huán)體。這種空語句在等待某個事件發(fā)生時特別有用。例如,下面這段程序是讀取8051單片機(jī)串行口數(shù)據(jù)的函數(shù),其中就用了一個空語句while (!RI);來等待單片機(jī)串行口接收結(jié)束。

#include <reg51.h>      /* 插入8051單片機(jī)的預(yù)定義文件 */
char _getkey ()         /* 函數(shù)定義 */
{                       /* 函數(shù)體開始 */
char c;                 /* 定義變量 */
while (!RI);            /* 空語句,等待8051單片機(jī)串行口接收結(jié)束 */
c = SBUF;               /* 讀串行口內(nèi)容 */
RI = 0;                 /* 清除串行口接收標(biāo)志 */
return (c);             /* 返回 */
}                       /* 函數(shù)體結(jié)束 */

采用分號“;”作為空語句使用時,要注意與簡單語句中有效組成部分的分號相區(qū)別。不能濫用空語句,以免引起程序的誤操作,甚至造成程序語法上的錯誤。

2.3.2 復(fù)合語句

復(fù)合語句是由若干條語句組合而成的一種語句,它是用一個大括號“{}”將若干條語句組合在一起而形成的一種功能塊。復(fù)合語句不需要以分號“;”結(jié)束,但它內(nèi)部的各條單語句仍需以分號“;”結(jié)束。復(fù)合語句的一般形式為:

{
  局部變量定義;
  語句1;
  語句2;
  …
  語句n;
}

復(fù)合語句在執(zhí)行時,其中的各條單語句依次順序執(zhí)行。整個復(fù)合語句在語法上等價于一條單語句,因此在C語言程序中可以將復(fù)合語句視為一條單語句。復(fù)合語句允許嵌套,即在復(fù)合語句內(nèi)部還可以包含別的復(fù)合語句。通常復(fù)合語句都出現(xiàn)在函數(shù)中,實際上,函數(shù)的執(zhí)行部分(即函數(shù)體)就是一個復(fù)合語句。復(fù)合語句中的單語句一般是可執(zhí)行語句,此外還可以是變量的定義語句(說明變量的數(shù)據(jù)類型)。

在復(fù)合語句內(nèi)所定義的變量,稱為該復(fù)合語句中的局部變量,它僅在當(dāng)前這個復(fù)合語句中有效。利用復(fù)合語句將多條單語句組合在一起,以及在復(fù)合語句中進(jìn)行局部變量定義是C語言的一個重要特征。

例2.12:復(fù)合語句及其局部變量的使用。

#include <stdio.h>
main() {                 /* 主函數(shù)體開始 */
    int a,b,c,d;         /* 定義變量a,b,c,d,它們在整個主函數(shù)中有效 */
    a = 1; b = 2; c = 3; d = 4;
    printf("\nX: %d %d %d %d",a,b,c,d);
    {                    /* 復(fù)合語句1 */
      int b,m;           /* 定義局部變量b,m,它們僅在復(fù)合語句1 中有效 */
      b = 8; m = 100;
      printf("\nY: %d %d %d %d | %d",a,b,c,d,m);
      {                  /* 復(fù)合語句2 */
      int c,n;           /* 定義局部變量c,n,它們僅在復(fù)合語句2中有效 */
      c = 9; n = 150;
      printf("\nZ: %d %d %d %d | %d %d",a,b,c,d,m,n);
      }                  /* 復(fù)合語句2 結(jié)束 */
      printf("\nY: %d %d %d %d | %d",a,b,c,d,m);
  }                      /* 復(fù)合語句1 結(jié)束 */
  printf("\nX: %d %d %d %d",a,b,c,d);
  printf("\n");
  while(1);
}                        /* 主函數(shù)體結(jié)束 */

程序執(zhí)行結(jié)果:

X: 1 2 3 4
Y: 1 8 3 4 | 100
Z: 1 8 9 4 | 100150
Y: 1 8 3 4 | 100
X: 1 2 3 4

在這個程序的主函數(shù)體開始處,定義了變量a,b,c,d,它們在整個主函數(shù)體中都是有效的,在主函數(shù)體中的復(fù)合語句1和復(fù)合語句2中都可以使用它們。另外,在復(fù)合語句1中又定義了局部變量m和與主函數(shù)體中定義的變量同名的局部變量b,這種局部變量m和b僅在定義它的復(fù)合語句1中有效,而且局部變量b的優(yōu)先級高于在主函數(shù)體中定義的同名變量b。因此在復(fù)合語句1中執(zhí)行printf函數(shù)輸出的b值為8,而不是2。

同樣,在復(fù)合語句2中定義了一個與主函數(shù)體中同名的局部變量c,在復(fù)合語句2中執(zhí)行printf函數(shù)輸出的c值為9,而不是3。一旦出了復(fù)合語句,則其中的局部變量立即失效。如果有同名的局部變量,則恢復(fù)該變量在上一層位置所定義的初值。讀者可以通過仔細(xì)分析本程序的執(zhí)行結(jié)果來弄清各個變量的作用范圍。

2.3.3 條件語句

條件語句又稱為分支語句,它是用關(guān)鍵字if構(gòu)成的。C語言提供了三種形式的條件語句。

(1)if(條件表達(dá)式)語句

其含義為:若條件表達(dá)式的結(jié)果為真(非0值),就執(zhí)行后面的語句;反之若條件表達(dá)式的結(jié)果為假(0值),就不執(zhí)行后面的語句。這里的語句也可以是復(fù)合語句。這種條件語句的執(zhí)行過程如圖2.1(a)所示。

(2)if(條件表達(dá)式)語句1

else語句2

其含義為:若條件表達(dá)式的結(jié)果為真(非0值),就執(zhí)行語句1;反之若條件表達(dá)式的結(jié)果為假(0值),就執(zhí)行語句2。這里的語句1和語句2均可以是復(fù)合語句。這種條件語句的執(zhí)行過程如圖2.1(b)所示。

圖2.1 條件語句的執(zhí)行過程

(3)if(條件表達(dá)式1)語句1

else if(條件式表達(dá)2)語句2

else if(條件式表達(dá)3)語句3

… …

else if(條件表達(dá)式n)語句m

else語句n

這種條件語句常用來實現(xiàn)多方向條件分支,其執(zhí)行過程如圖2.2所示。

圖2.2 多分支條件語句的執(zhí)行過程

例2.13:條件語句的使用——求一元二次方程的根。

#include <stdio.h>
#include <math.h>
main() {
  float a,b,c,x1,x2;
  float r,s;
  a = 2.0; b = 3.0; c = 4.0;
  r = b * b -4.0 * a * c;
  if( r > 0.0 )
    {
      s = sqrt(r);
      x1 = (-b + s) / (2.0 * a);
      x2 = (-b - s) / (2.0 * a);
      printf("real: x1 =%15.7f,x2 =%15.7f\n",x1,x2);
    }
  else if( r == 0.0 )
      printf("double: x1,x2 =%15.7f\n",-b/(2.0*a));
  else
    {
      x1 = -b / (2.0 * a);
      x2 = sqrt(-r) / (2.0 * a);
      printf("complex: Re=%15.7f,Im=%15.7f\n",x1,x2);
    }
    while(1);
}

程序執(zhí)行結(jié)果:

complex: Re= -0.7500000,Im= 1.1989580

在這個程序中使用了庫函數(shù)sqrt(r)來求方程的根,sqrt是一個算術(shù)庫函數(shù)。為了使程序能得到正確的編譯和執(zhí)行,在本程序的開始處使用了預(yù)處理命令#include將庫函數(shù)sqrt所在的預(yù)處理文件math.h包含到程序中去。

2.3.4 開關(guān)語句

開關(guān)語句也是一種用來實現(xiàn)多方向條件分支的語句。雖然采用條件語句也可以實現(xiàn)多方向條件分支,但是當(dāng)分支較多時會使條件語句的嵌套層次太多,程序冗長,可讀性降低。開關(guān)語句直接處理多分支選擇,使程序結(jié)構(gòu)清晰,使用方便。開關(guān)語句是用關(guān)鍵字switch構(gòu)成的,它的一般形式如下:

switch (表達(dá)式)
  {
    case  常量表達(dá)式1:語句1
                    break;
    case  常量表達(dá)式2:語句2
                    break;
  …              …
    case  常量表達(dá)式n:語句n
                    break;
    default:語句d
    }

開關(guān)語句的執(zhí)行過程是:將switch后面表達(dá)式的值與case后面各個常量表達(dá)式的值逐個進(jìn)行比較,若遇到匹配時,就執(zhí)行相應(yīng)case后面的語句,然后執(zhí)行break語句,break語句又稱間斷語句,它的功能是中止當(dāng)前語句的執(zhí)行,使程序跳出switch語句。若無匹配的情況,則只執(zhí)行語句d。開關(guān)語句的執(zhí)行過程如圖2.3所示。

圖2.3 開關(guān)語句的執(zhí)行過程

例2.14:開關(guān)語句的使用。

本程序按照輸入的年份year和月份month,計算該月有多少天。程序需要判斷該年是否為閏年。閏年的2月有29天,平年的2月只有28天。閏年的條件是:年份數(shù)year能被4整除,但不能被100整除;或者年份數(shù)year能被400整除。這個條件可以用一個邏輯關(guān)系式來表達(dá):

year%4= =0 && year%100 != 0 || year%400 = = 0

當(dāng)這個表達(dá)式的值為真(非0值)時,year為閏年,否則為平年。

#include <stdio.h>
main() {
    int year,month,len;
    while(1){
      printf("Enter year & month: \n");
          scanf("%d%d",&year,&month);
      switch(month) {
          case 1:  len=31; break;
          case 3:  len=31; break;
          case 5:  len=31; break;
          case 7:  len=31; break;
          case 8:  len=31; break;
          case 10: len=31; break;
          case 12: len=31; break;
          case 4:  len=30; break;
          case 6:  len=30; break;
          case 9:  len=30; break;
          case 11: len=30; break;
          case 2:  if(year%4==0&&year%100 != 0||year%400==0) len=29;
                  else len=28;
                      break;
              default: printf("Input error \n");
                      len=0;
                      break;
      }
          if(len != 0)
              printf("The lenth of%d,%dis %d \n",year,month,len);
    }
}

程序執(zhí)行結(jié)果:

Enter year & month:
1996  2 回車
The lenth of 1996,2 is 29

2.3.5 循環(huán)語句

實際應(yīng)用中很多地方需要用到循環(huán)控制,如需要反復(fù)進(jìn)行某種操作等,這時可以用循環(huán)語句來實現(xiàn)。在C語言程序中用來構(gòu)成循環(huán)控制的語句有:while語句、do-while語句、for語句以及goto語句,分述如下。

① 采用while語句構(gòu)成循環(huán)結(jié)構(gòu)的一般形式如下:

while(條件表達(dá)式) 語句;

其意義為,當(dāng)條件表達(dá)式的結(jié)果為真(非0值)時,程序就重復(fù)執(zhí)行后面的語句,一直執(zhí)行到條件表達(dá)式的結(jié)果變?yōu)榧伲?值)時為止。這種循環(huán)結(jié)構(gòu)是先檢查條件表達(dá)式所給出的條件,再根據(jù)檢查的結(jié)果決定是否執(zhí)行后面的語句。如果條件表達(dá)式的結(jié)果一開始就為假,則后面的語句一次也不會被執(zhí)行。這里的語句可以是復(fù)合語句。圖2.4所示為while語句的執(zhí)行過程。

圖2.4 while語句的執(zhí)行過程

例2.15:使用while語句計算自然數(shù)1~100的累加和。

#include<stdio.h>
main() {
    int i,s=0;
    i=1;
    while (i<=100)
    {                    /* 復(fù)合語句循環(huán)體 */
      s=s+i;
      i++;
    }                    /* 循環(huán)體結(jié)束 */
    printf("1+2+ … +100 = %d\n",s);
    while(1);
}

程序執(zhí)行結(jié)果:

1+2+ … +100 = 5050

② 采用do-while語句構(gòu)成循環(huán)結(jié)構(gòu)的一般形式如下:

do語句while(條件表達(dá)式);

這種循環(huán)結(jié)構(gòu)的特點(diǎn)是先執(zhí)行給定的循環(huán)體語句,然后再檢查條件表達(dá)式的結(jié)果。當(dāng)條件表達(dá)式的值為真(非0值)時,則重復(fù)執(zhí)行循環(huán)體語句,直到條件表達(dá)式的值變?yōu)榧伲?值)時為止。因此,用do-while語句構(gòu)成的循環(huán)結(jié)構(gòu)在任何條件下,循環(huán)體語句至少會被執(zhí)行一次。圖2.5給出了這種循環(huán)結(jié)構(gòu)的流程圖。

圖2.5 do-while循環(huán)結(jié)構(gòu)的流程圖

例2.16:用do-while語句構(gòu)成的循環(huán)計算自然數(shù)1~100的累加和。

#include<stdio.h>
main() {
    int i,s=0;
    i=1;
    do
      {                 /* 復(fù)合語句循環(huán)體 */
      s=s+i;
      i++;
    }                   /* 循環(huán)體結(jié)束 */
  while(i<=100);
  printf("1+2+ … +100 = %d\n",s);
  while(1);
}

程序執(zhí)行結(jié)果:

1+2+ ··· +100 = 5050

例2.16的程序與例2.15的程序十分相似。它們的區(qū)別僅僅是執(zhí)行循環(huán)體語句和判斷條件表達(dá)式的結(jié)果的順序不同。另外,用do-while語句構(gòu)成的循環(huán)結(jié)構(gòu)中,while(條件表達(dá)式)的后面必須有一個分號,而用while語句構(gòu)成的循環(huán)結(jié)構(gòu)中while(條件表達(dá)式)后面是沒有分號的。這一點(diǎn)在寫程序時一定要注意。

③ 采用for語句構(gòu)成循環(huán)結(jié)構(gòu)的一般形式如下:

for([初值設(shè)定表達(dá)式];[循環(huán)條件表達(dá)式];[更新表達(dá)式])語句

for語句的執(zhí)行過程是:先計算出初值設(shè)定表達(dá)式的值,以此作為循環(huán)控制變量的初值,再檢查循環(huán)條件表達(dá)式的結(jié)果,當(dāng)滿足條件時就執(zhí)行循環(huán)體語句并計算更新表達(dá)式,然后再根據(jù)更新表達(dá)式的計算結(jié)果來判斷循環(huán)條件是否滿足,一直進(jìn)行到循環(huán)條件表達(dá)式的結(jié)果為假(0值)時退出循環(huán)體。for語句的執(zhí)行過程如圖2.6所示。

圖2.6 for語句的執(zhí)行過程

例2.17:用for語句構(gòu)成的循環(huán)計算自然數(shù)1~100的累加和。

#include<stdio.h>
main() {
    int i,s=0;
    for (i=1; i<=100; i++)
      s=s+i;                  /* 循環(huán)體語句 */
    printf("1+2+ … +100 = %d\n",s);
    while(1);
}

程序執(zhí)行結(jié)果:

1+2+…+100 = 5050

在C語言程序的循環(huán)結(jié)構(gòu)中,for語句的使用最為靈活,它不僅可以用于循環(huán)次數(shù)已經(jīng)確定的情況,而且可以用于循環(huán)次數(shù)不確定而只給出循環(huán)結(jié)束條件的情況。另外,for語句中的三個表達(dá)式是相互獨(dú)立的,并不一定要求三個表達(dá)式之間有依賴關(guān)系。并且for語句中的三個表達(dá)式都可能缺省,但無論缺省哪一個表達(dá)式,其中的兩個分號都不能缺省。一般不要缺省循環(huán)條件表達(dá)式,以免形成死循環(huán)。

例2.18:for語句中缺省表達(dá)式的例子——計算自然數(shù)1~100的累加和。

#include<stdio.h>
main() {
    int i,s=0;
    i=1;                    /* 設(shè)置循環(huán)初值 */
    for ( ;i<=100 ;) {    /* 缺省初值設(shè)定表達(dá)式和更新表達(dá)式 */
      s=s+i;                /* 循環(huán)體語句 */
      i++;                  /* 循環(huán)控制變量更新 */
      }
    printf("1+2+ … +100 = %d\n",s);
    while(1);
}

程序執(zhí)行結(jié)果:

1+2+…+100 = 5050

④ goto語句是一個無條件轉(zhuǎn)向語句,它的一般形式為:

goto語句標(biāo)號;

其中語句標(biāo)號是一個帶冒號“:”的標(biāo)識符。將goto語句和if語句一起使用,可以構(gòu)成一個循環(huán)結(jié)構(gòu)。但更常見的是在C語言程序中采用goto語句來跳出多重循環(huán),需要注意的是只能用goto語句從內(nèi)層循環(huán)跳到外層循環(huán),而不允許從外層循環(huán)跳到內(nèi)層循環(huán)。

例2.19:使用goto語句跳出循環(huán)結(jié)構(gòu)。

本程序采用循環(huán)結(jié)構(gòu)來求一整數(shù)的等差數(shù)列,該數(shù)列滿足條件:頭四個數(shù)的和值為26,積值為880。該數(shù)列的公差應(yīng)為正整數(shù),否則將產(chǎn)生負(fù)的項,此外該數(shù)列的首項數(shù)必須小于5,且其公差也應(yīng)小于5,否則頭四項的和值將大于26。

#include<stdio.h>
main() {
    int a,b,c,d,i;
    for(a=1; a<5; ++a) {
      for(d=1; d<5; ++d) {
        b=a+(a+d)+(a+2*d)+(a+3*d);
        c=a*(a+d)*(a+2*d)*(a+3*d);
        if(b==26 && c==880)
          goto pt;
      }
    }
    pt: for(i=0; i<=10; ++i)
          printf("%d,",a+i*d);
    printf("…\n");
    while(1);
}

程序執(zhí)行結(jié)果:

2,5,8,11,14,17,20,23,26,29,…

在這個程序中采用for語句構(gòu)成了兩重循環(huán)嵌套,即在第一個for語句的循環(huán)體中又出現(xiàn)了另一個for語句的循環(huán)體,需要時還可以構(gòu)成多重循環(huán)結(jié)構(gòu)。程序在最內(nèi)層循環(huán)體中采用了一個goto語句,它的作用是直接跳出兩層循環(huán),即跳到第一層循環(huán)體外邊由標(biāo)號pt:所指出的地方。前面在介紹開關(guān)語句時提到采用break語句可以跳出開關(guān)語句,break語句還可以用于跳出循環(huán)語句。對于上面的例子,也可以采用break語句來終止循環(huán)。

例2.20:用break語句終止循環(huán)。

#include<stdio.h>
main() {
    int a,b,c,d,i;
    for(a=1; a<5; ++a) {
      for(d=1; d<5; ++d) {
        b=a+(a+d)+(a+2*d)+(a+3*d);
        c=a*(a+d)*(a+2*d)*(a+3*d);
        if(b==26 && c==880)
          break;
        }
        if(b==26 && c==880)
          break;
      }
    for(i=0; i<=10; ++i)
      printf("%d,",a+i*d);
    printf("…\n");
    while(1);
}

程序執(zhí)行結(jié)果:

2,5,8,11,14,17,20,23,26,29,…

從例2.20可以看到,對于多重循環(huán)的情況,break語句只能跳出它所處的那一層循環(huán),而不像goto語句可以直接從最內(nèi)層循環(huán)中跳出來。由此可見,要退出多重循環(huán)時,采用goto語句比較方便。需要指出的是,break語句只能用于開關(guān)語句和循環(huán)語句之中,它是一種具有特殊功能的無條件轉(zhuǎn)移語句。另外還要注意,在進(jìn)行實際程序設(shè)計時,為了保證程序具有良好的結(jié)構(gòu),應(yīng)當(dāng)盡可能少地采用goto語句,以使程序結(jié)構(gòu)清晰易讀。

在循環(huán)結(jié)構(gòu)中還可以使用一種中斷語句continue,它的功能是結(jié)束本次循環(huán),即跳過循環(huán)體中下面尚未執(zhí)行的語句,把程序流程轉(zhuǎn)移到當(dāng)前循環(huán)語句的下一個循環(huán)周期,并根據(jù)循環(huán)控制條件決定是否重復(fù)執(zhí)行該循環(huán)體。continue語句的一般形式為:

continue;

continue語句通常和條件語句一起用在由while、do-while和for語句構(gòu)成的循環(huán)結(jié)構(gòu)中,它也是一種具有特殊功能的無條件轉(zhuǎn)移語句,但與break語句不同,continue語句并不跳出循環(huán)體,而只是根據(jù)循環(huán)控制條件確定是否繼續(xù)執(zhí)行循環(huán)語句。

例2.21:利用continue語句把10~20之間不能被3整除的數(shù)輸出。

#include<stdio.h>
main() {
    int n;
    for(n=10; n<=20; n++) {
      if(n%3==0)
        continue;
      printf("%d ",n);
      }
    while(1);
}

程序執(zhí)行結(jié)果:

10  11  13  14  16  17  19  20

2.3.6 返回語句

返回語句用于終止函數(shù)的執(zhí)行,并控制程序返回到調(diào)用該函數(shù)時所處的位置。返回語句有兩種形式:

return(表達(dá)式);
或者return;

如果return語句后邊帶有表達(dá)式,則要計算表達(dá)式的值,并將表達(dá)式的值作為該函數(shù)的返回值。若使用不帶表達(dá)式的第2種形式,則被調(diào)用函數(shù)返回主調(diào)用函數(shù)時,函數(shù)值不確定。一個函數(shù)的內(nèi)部可以含有多個return語句,但程序僅執(zhí)行其中的一個return語句而返回主調(diào)用函數(shù)。一個函數(shù)的內(nèi)部也可以沒有return語句,在這種情況下,當(dāng)程序執(zhí)行到最后一個界限符“}”處時,就自動返回主調(diào)用函數(shù)。

例2.22:return語句的使用。

#include <stdio.h>
main() {                                 /* 主函數(shù)體 */
    int x,n,p,power(int x,int n);
    printf("calculate X to the power of N\n please input X and N ? ");
    scanf("%d%d",&x,&n);
    p = power(x,n);                      /* 在此處調(diào)用函數(shù) */
    printf("%d to the power of %d = %d\n",x,n,p);
    while(1);
}
int power(int x,int n)                   /* 被調(diào)用函數(shù) */
int x,n;
{
    int i,p = 1;
    for( i=0; i<n; ++i ) p *= x;
    return( p );                         /* 帶值返回到調(diào)用處 */
}

程序執(zhí)行結(jié)果:

calculate X to the power of N
please input X and N ?  5 3  回車
5 to the power of 3 = 125
主站蜘蛛池模板: 收藏| 外汇| 若尔盖县| 江山市| 榆树市| 隆子县| 汉沽区| 溆浦县| 大同县| 满洲里市| 三江| 丁青县| 石门县| 肥乡县| 宁海县| 淮安市| 四会市| 平果县| 桓仁| 西贡区| 宿松县| 玛纳斯县| 江源县| 三江| 兴文县| 芒康县| 枞阳县| 昌乐县| 镇平县| 微博| 大石桥市| 苏尼特左旗| 休宁县| 濮阳市| 鹿泉市| 石台县| 田阳县| 汽车| 广平县| 翁牛特旗| 内黄县|