- Keil Cx51 V7.0單片機(jī)高級(jí)語(yǔ)言編程與μVision2應(yīng)用實(shí)踐
- 徐愛鈞 彭秀華編著
- 1207字
- 2018-12-29 19:18:28
第2章 Cx51程序設(shè)計(jì)基礎(chǔ)
Keil Cx51是一種專為8051單片機(jī)設(shè)計(jì)的高級(jí)語(yǔ)言C編譯器,支持符合ANSI標(biāo)準(zhǔn)的C語(yǔ)言進(jìn)行程序設(shè)計(jì),同時(shí)針對(duì)8051單片機(jī)自身特點(diǎn)作了一些特殊擴(kuò)展。為了幫助以前慣于使用匯編語(yǔ)言編程的單片機(jī)用戶盡快掌握Cx51編程技術(shù),本章對(duì)C語(yǔ)言的一些基本知識(shí)結(jié)合Cx51特點(diǎn)進(jìn)行闡述。
2.1 標(biāo)識(shí)符與關(guān)鍵字
C語(yǔ)言的標(biāo)識(shí)符是用來(lái)標(biāo)識(shí)源程序中某個(gè)對(duì)象名字的。這些對(duì)象可以是函數(shù)、變量、常量、數(shù)組、數(shù)據(jù)類型、存儲(chǔ)方式、語(yǔ)句等。一個(gè)標(biāo)識(shí)符由字母、數(shù)字和下劃線等組成,第一個(gè)字符必須是字母或下劃線。C語(yǔ)言是對(duì)大小寫字母敏感的,如“max”與“MAX”是兩個(gè)完全不同的標(biāo)識(shí)符。程序中對(duì)于標(biāo)識(shí)符的命名應(yīng)當(dāng)簡(jiǎn)潔明了,含義清晰,便于閱讀理解,如用標(biāo)識(shí)符“max”表示最大值,用“TIMER0”表示定時(shí)器0等。
關(guān)鍵字是一類具有固定名稱和特定含義的特殊標(biāo)識(shí)符,有時(shí)又稱為保留字。在編寫C語(yǔ)言源程序時(shí)一般不允許將關(guān)鍵字另作他用,換句話說(shuō)就是對(duì)于標(biāo)識(shí)符的命名不要與關(guān)鍵字相同。與其他計(jì)算機(jī)語(yǔ)言相比,C語(yǔ)言的關(guān)鍵字是比較少的,ANSI C標(biāo)準(zhǔn)一共規(guī)定了32個(gè)關(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è)計(jì)的基本語(yǔ)法
雖然C語(yǔ)言對(duì)語(yǔ)法的限制不太嚴(yán)格,用戶在編寫程序時(shí)有較大的自由,但它畢竟還是一種程序設(shè)計(jì)語(yǔ)言,與其他計(jì)算機(jī)語(yǔ)言一樣,采用C語(yǔ)言進(jìn)行程序設(shè)計(jì)時(shí),仍需要遵從一定的語(yǔ)法規(guī)則。
2.2.1 數(shù)據(jù)類型
任何程序設(shè)計(jì)都離不開對(duì)于數(shù)據(jù)的處理,一個(gè)程序如果沒有數(shù)據(jù),它就無(wú)法工作。數(shù)據(jù)在計(jì)算機(jī)內(nèi)存中的存放情況由數(shù)據(jù)結(jié)構(gòu)決定。C語(yǔ)言的數(shù)據(jù)結(jié)構(gòu)是以數(shù)據(jù)類型出現(xiàn)的,數(shù)據(jù)類型可分為基本數(shù)據(jù)類型和復(fù)雜數(shù)據(jù)類型,復(fù)雜數(shù)據(jù)類型由基本數(shù)據(jù)類型構(gòu)造而成。C語(yǔ)言中的基本數(shù)據(jù)類型有char,int,short,long,float和double。對(duì)于Cx51編譯器來(lái)說(shuō),short類型與int類型相同,double類型與float類型相同。
1.char:字符型
有signed char(帶符號(hào)數(shù))和unsigned char(無(wú)符號(hào)數(shù))之分,默認(rèn)值為signed char。它們的長(zhǎng)度均為一個(gè)字節(jié),用于存放一個(gè)單字節(jié)的數(shù)據(jù)。對(duì)于singed char類型數(shù)據(jù),其字節(jié)中的最高位表示該數(shù)據(jù)的符號(hào),“0”表示正數(shù),“1”表示負(fù)數(shù)。負(fù)數(shù)用補(bǔ)碼表示,數(shù)值的表示范圍是-128~+127;對(duì)于unsigned char類型數(shù)據(jù),其字節(jié)中的所有位均用來(lái)表示數(shù)據(jù)的數(shù)值,數(shù)值的表示范圍是0~255。
2.int:整型
有signed int和unsigned int之分,默認(rèn)值為signed int。它們的長(zhǎng)度均為兩個(gè)字節(jié),用于存放一個(gè)雙字節(jié)的數(shù)據(jù)。signed int是有符號(hào)整型數(shù),字節(jié)中的最高位表示數(shù)據(jù)的符號(hào),“0”表示正數(shù),“1”表示負(fù)數(shù)。所能表示的數(shù)值范圍是-32768~+32767。unsigned int是無(wú)符號(hào)整型數(shù),所能表示的數(shù)值范圍是0~65535。
3.long:長(zhǎng)整型
有signed long和unsigned long之分,默認(rèn)值為signed long。它們的長(zhǎng)度均為四個(gè)字節(jié)。signed long是有符號(hào)的長(zhǎng)整型數(shù)據(jù),字節(jié)中的最高位表示數(shù)據(jù)的符號(hào),“0”表示正數(shù),“1”表示負(fù)數(shù),數(shù)值的表示范圍是-2147 483648~+2147 483647。unsigned long是無(wú)符號(hào)長(zhǎng)整型數(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ù)占用四個(gè)字節(jié)(32位二進(jìn)制數(shù)),在內(nèi)存中的存放格式如下:

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

需要指出的是,對(duì)于浮點(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ù))時(shí)即為非正常值:
0xFFFFFFFF非數(shù)(NaN)
0x7F800000正溢出(+INF)
0xFF800000負(fù)溢出(-INF)
另外,由于8051單片機(jī)不包括捕獲浮點(diǎn)運(yùn)算錯(cuò)誤的中斷向量,因此必須由用戶自己根據(jù)可能出現(xiàn)的錯(cuò)誤條件用軟件來(lái)進(jìn)行適當(dāng)?shù)奶幚怼?/p>
除了以上四種基本數(shù)據(jù)類型之外,還有以下一些數(shù)據(jù)類型。
5.*:指針型
指針型數(shù)據(jù)不同于以上四種基本數(shù)據(jù)類型,它本身是一個(gè)變量,但在這個(gè)變量中存放的不是普通數(shù)據(jù)而是指向另一個(gè)數(shù)據(jù)的地址。指針變量也要占據(jù)一定的內(nèi)存單元,在C51中指針變量的長(zhǎng)度一般為1~3個(gè)字節(jié)。指針變量也具有類型,其表示方法是在指針符號(hào)“*”的前面冠以數(shù)據(jù)類型符號(hào),如char * point1表示point1是一個(gè)字符型的指針變量;float* point2表示point2是一個(gè)浮點(diǎn)型的指針變量。指針變量的類型表示該指針?biāo)赶虻刂分袛?shù)據(jù)的類型。使用指針型變量可以方便地對(duì)8051單片機(jī)各部分物理地址直接進(jìn)行操作。
6.bit:位類型
這是Keil Cx51編譯器的一種擴(kuò)充數(shù)據(jù)類型,利用它可定義一個(gè)位變量,但不能定義位指針,也不能定義位數(shù)組。
7.sfr:特殊功能寄存器
這也是Keil Cx51編譯器的一種擴(kuò)充數(shù)據(jù)類型,利用它可以定義8051單片機(jī)的所有內(nèi)部8位特殊功能寄存器。sfr型數(shù)據(jù)占用一個(gè)內(nèi)存單元,其取值范圍是0~255。
8.sfr16:16位特殊功能寄存器
它占用兩個(gè)內(nèi)存單元,取值范圍是0~65535,利用它可以定義8051單片機(jī)內(nèi)部16位特殊功能寄存器。
9.sbit:可尋址位
這也是Keil Cx51編譯器的一種擴(kuò)充數(shù)據(jù)類型,利用它可以定義8051單片機(jī)內(nèi)部RAM中的可尋址位或特殊功能寄存器中的可尋址位。
例如,采用如下語(yǔ)句:
sfr P0=80H; sbit FLAG1=P0^1;
可以將8051單片機(jī)P0口地址定義為80H,將P0.1位定義為FLAG1。
表2-3列出了Keil Cx51編譯器能夠識(shí)別的數(shù)據(jù)類型。
表2-3 Keil Cx51編譯器能夠識(shí)別的數(shù)據(jù)類型

在C語(yǔ)言程序的表達(dá)式或變量賦值運(yùn)算中,有時(shí)會(huì)出現(xiàn)運(yùn)算對(duì)象的數(shù)據(jù)不一致的情況,C語(yǔ)言允許任何標(biāo)準(zhǔn)數(shù)據(jù)類型之間的隱式轉(zhuǎn)換。隱式轉(zhuǎn)換按以下優(yōu)先級(jí)別自動(dòng)進(jìn)行:
bit→char→int→long→float
signed→unsigned
其中箭頭方向僅表示數(shù)據(jù)類型級(jí)別的高低,轉(zhuǎn)換時(shí)由低向高進(jìn)行,而不是數(shù)據(jù)轉(zhuǎn)換時(shí)的順序。例如,將一個(gè)bit(位類型)變量賦給一個(gè)int(整型變量)時(shí),不需要先將bit型變量轉(zhuǎn)換成char型之后再轉(zhuǎn)換成int型,而是將bit型變量直接轉(zhuǎn)換成int型并完成賦值運(yùn)算。一般來(lái)說(shuō),如果有幾個(gè)不同類型的數(shù)據(jù)同時(shí)參加運(yùn)算,先將低級(jí)別類型的數(shù)據(jù)轉(zhuǎn)換成高級(jí)別類型,再作運(yùn)算處理,并且運(yùn)算結(jié)果為高級(jí)別類型數(shù)據(jù)。C語(yǔ)言除了能對(duì)數(shù)據(jù)類型作自動(dòng)的隱式轉(zhuǎn)換之外,還可以采用強(qiáng)制類型轉(zhuǎn)換符“()”對(duì)數(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í)行過(guò)程中不能改變。常量的數(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。
長(zhǎng)整數(shù):在數(shù)字后面加一個(gè)字母L就構(gòu)成了長(zhǎng)整數(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)容為可選項(xiàng),根據(jù)具體情況可有可無(wú),但其余部分必須有。如123e4、5e6、-7.0e-8等都是合法的指數(shù)形式浮點(diǎn)型常量;而e9、5e4.3和e都是不合法的表示形式。
3.字符型常量
字符型常量是單引號(hào)內(nèi)的字符,如'a','b'等。對(duì)于不可顯示的控制字符,可以在該字符前面加一個(gè)反斜杠“\”組成轉(zhuǎn)義字符。利用轉(zhuǎn)義字符可以完成一些特殊功能和輸出時(shí)的格式控制。常用轉(zhuǎn)義字符如表2-4所示。
表2-4 常用轉(zhuǎn)義字符表

4.字符串型常量
字符串型常量由雙引號(hào)""內(nèi)的字符組成,如"ABCD"、"$1234"等都是字符串常量。當(dāng)雙引號(hào)內(nèi)的字符個(gè)數(shù)為0時(shí),稱為空串常量。需要注意的是,字符串常量首尾的雙引號(hào)是界限符,當(dāng)需要表示雙引號(hào)字符串時(shí),可用雙引號(hào)轉(zhuǎn)義字符“\"”來(lái)表示。另外,C語(yǔ)言將字符串常量作為一個(gè)字符類型數(shù)組來(lái)處理,在存儲(chǔ)字符串常量時(shí)要在字符串的尾部加一個(gè)轉(zhuǎn)義字符\0作為該字符串常量的結(jié)束符。因此不要將字符常量與字符串常量混淆,如字符常量'a'與字符串常量"a"是不一樣的。
2.2.3 變量及其存儲(chǔ)模式
變量是一種在程序執(zhí)行過(guò)程中其值能不斷變化的量。使用一個(gè)變量之前,必須進(jìn)行定義,用一個(gè)標(biāo)識(shí)符作為變量名并指出它的數(shù)據(jù)類型和存儲(chǔ)模式,以便編譯系統(tǒng)為它分配相應(yīng)的存儲(chǔ)單元。在Cx51中對(duì)變量進(jìn)行定義的格式如下:
[存儲(chǔ)種類]數(shù)據(jù)類型 [存儲(chǔ)器類型]變量名表;
其中,“存儲(chǔ)種類”和“存儲(chǔ)器類型”是可選項(xiàng)。變量的存儲(chǔ)種類有四種:自動(dòng)(auto)、外部(extern)、靜態(tài)(static)和寄存器(register)。定義一個(gè)變量時(shí)如果省略存儲(chǔ)種類選項(xiàng),則該變量將為自動(dòng)(auto)變量。定義一個(gè)變量時(shí)除了需要說(shuō)明其數(shù)據(jù)類型之外,Keil Cx51編譯器還允許說(shuō)明變量的存儲(chǔ)器類型。Keil Cx51編譯器完全支持8051系列單片機(jī)的硬件結(jié)構(gòu)和存儲(chǔ)器組織,對(duì)于每個(gè)變量可以準(zhǔn)確地賦予其存儲(chǔ)器類型,使之能夠在單片機(jī)系統(tǒng)內(nèi)準(zhǔn)確地定位。表2-5列出了Keil Cx51編譯器所能識(shí)別的存儲(chǔ)器類型。
表2-5 Keil Cx51編譯器所能識(shí)別的存儲(chǔ)器類型

定義變量時(shí)如果省略“存儲(chǔ)器類型”選項(xiàng),則按編譯時(shí)使用的存儲(chǔ)器模式SMALL、COMPACT或LARGE來(lái)規(guī)定默認(rèn)存儲(chǔ)器類型,確定變量的存儲(chǔ)器空間,函數(shù)中不能采用寄存器傳遞的參數(shù)變量和過(guò)程變量也保存在默認(rèn)的存儲(chǔ)器空間。Keil Cx51編譯器的三種存儲(chǔ)器模式(默認(rèn)的存儲(chǔ)器類型)對(duì)變量的影響如下。
1.SMALL
變量被定義在8051單片機(jī)的片內(nèi)數(shù)據(jù)存儲(chǔ)器中,對(duì)這種變量的訪問(wèn)速度最快。另外,所有的對(duì)象,包括堆棧,都必須位于片內(nèi)數(shù)據(jù)存儲(chǔ)器中,而堆棧的長(zhǎng)度是很重要的,實(shí)際棧長(zhǎng)取決于不同函數(shù)的嵌套深度。
2.COMPACT
變量被定義在分頁(yè)尋址的片外數(shù)據(jù)存儲(chǔ)器中,每一頁(yè)片外數(shù)據(jù)存儲(chǔ)器的長(zhǎng)度為256字節(jié)。這時(shí)對(duì)變量的訪問(wèn)是通過(guò)寄存器間接尋址(MOVX @Ri)進(jìn)行的,堆棧位于8051單片機(jī)片內(nèi)數(shù)據(jù)存儲(chǔ)器中。采用這種編譯模式時(shí),變量的高8位地址由P2口確定,低8位地址由R0或R1的內(nèi)容決定。采用這種模式的同時(shí),必須適當(dāng)改變啟動(dòng)配置文件STARTUP.A51中的參數(shù):PDATASTART和PDATALEN;在用BL51進(jìn)行連接時(shí)還必須采用連接控制命令“PDATA”對(duì)P2口地址進(jìn)行定位,這樣才能確保P2口為所需要的高8位地址。
3.LARGE
變量被定義在片外數(shù)據(jù)存儲(chǔ)器中(最大可達(dá)64KB),使用數(shù)據(jù)指針DPTR來(lái)間接訪問(wèn)變量(MOVX @DPTR)。這種訪問(wèn)數(shù)據(jù)的方法效率是不高的,尤其是對(duì)于2個(gè)以上字節(jié)的變量,用這種方法相當(dāng)影響程序的代碼長(zhǎng)度。
需要特別指出的是,變量的存儲(chǔ)種類與存儲(chǔ)器類型是完全無(wú)關(guān)的。例如:
static unsigned char data x; /* 在片內(nèi)數(shù)據(jù)存儲(chǔ)器中定義一個(gè)靜態(tài)無(wú)符 號(hào)字符型變量x */ int y; /* 定義一個(gè)自動(dòng)整型變量y,它的存儲(chǔ)器類型由 編譯模式確定 */
8051系列單片機(jī)具有多種內(nèi)部寄存器,其中一些是特殊功能寄存器,如定時(shí)器方式控制寄存器TMOD、中斷允許控制寄存器IE等。為了能夠直接訪問(wèn)這些特殊功能寄存器,Keil Cx51編譯器擴(kuò)充了關(guān)鍵字sfr和sfr16,利用這種擴(kuò)充關(guān)鍵字可以在C語(yǔ)言源程序中直接對(duì)8051單片機(jī)的特殊功能寄存器進(jìn)行定義。定義方法如下:
sfr特殊功能寄存器名=地址常數(shù);
例如:
sfr P0 = 0x80; /* 定義I/O口P0,其地址為0x80 */
這里需要注意的是,在關(guān)鍵字sfr后面必須跟一個(gè)標(biāo)識(shí)符作為寄存器名,名字可任意選取,但應(yīng)符合一般習(xí)慣。等號(hào)后面必須是常數(shù),不允許有帶運(yùn)算符的表達(dá)式,而且該常數(shù)必須在特殊功能寄存器的地址范圍之內(nèi)(0x80~0xFF)。在新一代的8051單片機(jī)中,特殊功能寄存器經(jīng)常組合成16位來(lái)使用。采用關(guān)鍵字sfr16可以定義這種16位的特殊功能寄存器。例如,對(duì)于8052單片機(jī)的定時(shí)器T2,可采用如下的方法來(lái)定義:
sfr16 T2 = 0xCC; /* 定義TIMER2,其地址為T2L=0xCC,T2H=0xCD */
這里T2為特殊功能寄存器名,等號(hào)后面是它的低字節(jié)地址,其高字節(jié)地址必須在物理上直接位于低字節(jié)之后。這種定義方法適用于所有新一代的8051單片機(jī)中新增加的特殊功能寄存器。
在8051單片機(jī)應(yīng)用系統(tǒng)中經(jīng)常需要訪問(wèn)特殊功能寄存器中的某些位,Keil Cx51編譯器為此提供了一個(gè)擴(kuò)充關(guān)鍵字sbit,利用它定義可位尋址對(duì)象。定義方法有如下三種。
(1)sbit位變量名=位地址
這種方法將位的絕對(duì)地址賦給位變量,位地址必須位于0x80~0xFF之間。例如:
sbit OV = 0xD2; sbit CY = 0xD7;
(2)sbit位變量名=特殊功能寄存器名^位位置
當(dāng)可尋址位位于特殊功能寄存器中時(shí)可采用這種方法,“位位置”是一個(gè)0~7之間的常數(shù)。例如:
sfr PSW = 0xD0; sbit OV = PSW^2; sbit CY = PSW^7;
(3)sbit位變量名=字節(jié)地址^位位置
這種方法以一個(gè)常數(shù)(字節(jié)地址)作為基地址,該常數(shù)必須在0x80H~0xFF之間。“位位置”是一個(gè)0~7之間的常數(shù)。例如:
sbit OV = 0xD0^2; sbit CY = 0xD0^7;
當(dāng)位對(duì)象位于8051單片機(jī)片內(nèi)存儲(chǔ)器中可位尋址區(qū)時(shí)稱之為“可位尋址對(duì)象”。Keil Cx51編譯器提供了一個(gè)bdata存儲(chǔ)器類型,允許將具有bdata類型的對(duì)象放入8051單片機(jī)片內(nèi)可位尋址區(qū)。例如:
int bdata ibase; /*在位尋址區(qū)定義一個(gè)整型變量ibase */ char bdata bary[4]; /*在位尋址區(qū)定義一個(gè)數(shù)組array[4] */
使用關(guān)鍵字sbit可以獨(dú)立訪問(wèn)可位尋址對(duì)象中的某一位。例如:
sbit mybit0=ibase^0; sbit mybit15=ibase^15; sbit Ary07=bary[0]^7; sbit Ary37=bary[3]^7;
采用這種方法定義可位尋址變量時(shí)要求基址對(duì)象的存儲(chǔ)器類型為bdata,操作符“^”后面“位位置”的最大值取決于指定的基地址類型,對(duì)于char類型來(lái)說(shuō)是0~7;對(duì)于int類型來(lái)說(shuō)是0~15;對(duì)于long類型來(lái)說(shuō)是0~31。
需要注意的是,sbit是一個(gè)獨(dú)立的關(guān)鍵字,不要將它與關(guān)鍵字bit相混淆。關(guān)鍵字bit是Keil Cx51編譯器的一種擴(kuò)充數(shù)據(jù)類型,用來(lái)定義一個(gè)普通位變量,它的值是二進(jìn)制數(shù)的0或1。一個(gè)函數(shù)中可以包含bit類型的參數(shù),函數(shù)的返回值也可為bit類型。例如:
static bit direction_bit /* 定義一個(gè)靜態(tài)位變量direction_bit */ extern bit lock_prt_port /* 定義一個(gè)外部位變量lock_prt_port */ bit bfunc(bit b0,bit b1); /* 定義一個(gè)返回位型值的函數(shù)bfunc,函 {... 數(shù)中包含有兩個(gè)位型參數(shù)b0 和b1 */ return(b1) /* 返回一個(gè)位型值b1 */ }
如果在函數(shù)中禁止使用中斷(#pragma disable)或者函數(shù)中包含有明確的寄存器組切換(using n),則該函數(shù)不能返回位型值,否則在編譯時(shí)會(huì)產(chǎn)生編譯錯(cuò)誤。另外,不能定義位指針,也不能定義位數(shù)組。
上面介紹了變量及其定義方法,這在編寫C語(yǔ)言程序時(shí)是十分重要的。從變量的作用范圍來(lái)看,還有全局變量和局部變量之分。全局變量是指在程序開始處或各個(gè)功能函數(shù)的外面定義的變量,在程序開始處定義的全局變量對(duì)于整個(gè)程序都有效,可供程序中所有函數(shù)共同使用;而在各功能函數(shù)外面定義的全局變量只對(duì)從定義處開始往后的各個(gè)函數(shù)有效,只有從定義處往后的那些功能函數(shù)才可以使用該變量,定義處前面的函數(shù)則不能使用。
局部變量是指在函數(shù)內(nèi)部或以花括號(hào){ }圍起來(lái)的功能塊內(nèi)部所定義的變量,局部變量只在定義它的函數(shù)或功能塊以內(nèi)有效,在該函數(shù)或功能塊以外則不能使用。局部變量可以與全局變量同名,但在這種情況下局部變量的優(yōu)先級(jí)較高,而同名的全局變量在該功能塊內(nèi)被暫時(shí)屏蔽。
從變量的存在時(shí)間來(lái)看又可分為靜態(tài)存儲(chǔ)變量和動(dòng)態(tài)存儲(chǔ)變量。
靜態(tài)存儲(chǔ)變量是指該變量在程序運(yùn)行期間其存儲(chǔ)空間固定不變;動(dòng)態(tài)存儲(chǔ)變量是指該變量的存儲(chǔ)空間不確定,在程序運(yùn)行期間根據(jù)需要?jiǎng)討B(tài)地為該變量分配存儲(chǔ)空間。一般來(lái)說(shuō),全局變量為靜態(tài)存儲(chǔ)變量,局部變量為動(dòng)態(tài)存儲(chǔ)變量。
在進(jìn)行程序設(shè)計(jì)的時(shí)候經(jīng)常需要給一些變量賦以初值,C語(yǔ)言允許在定義變量的同時(shí)給變量賦初值。下面是一些變量定義的例子。
char data var1; /* 在data區(qū)定義字符型變量var1 */ int idata var2; /* 在idata區(qū)定義整型變量var2 */ int a=5; /* 定義變量a,同時(shí)賦以初值5,變量a位于 由編譯模式確定的默認(rèn)存儲(chǔ)區(qū) */ char code text[ ]="ENTER PARAMETER:"; /* 在code區(qū)定義字符串?dāng)?shù)組 */ unsigned char xdata vecter [10][4][4]; /* 在xdata區(qū)定義無(wú)符號(hào)字符型三維數(shù)組 變量vecter[10][4][4] */ static unsigned long xdata array [100]; /* 在xdata區(qū)定義靜態(tài)無(wú)符號(hào)長(zhǎng)整型數(shù)組 變量array[100]*/ extern float idata x,y,z; /* 在idata區(qū)定義外部浮點(diǎn)型變量x,y,z*/ char xdata * px; /* 在xdata區(qū)定義一個(gè)指向?qū)ο箢愋蜑? char的指針px,指針px自身在默認(rèn)存 儲(chǔ)區(qū)(由編譯模式確定),長(zhǎng)度為2字節(jié) (0~0xFFFF)*/ char xdata * data pdx; /* 除了指針明確定位于內(nèi)部數(shù)據(jù)存儲(chǔ)器區(qū) (data)之外,與上例完全相同,由于指定 了存儲(chǔ)器類型,所以與編譯模式無(wú)關(guān) */ extern bit data lock_prt_port; /* 在data區(qū)定義一個(gè)外部位變量 */ 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語(yǔ)言程序中除了可以采用上面所介紹的數(shù)據(jù)類型之外,用戶還可以根據(jù)自己的需要對(duì)數(shù)據(jù)類型重新定義。重新定義時(shí)需用到關(guān)鍵字typedef,定義方法如下:
typedef 已有的數(shù)據(jù)類型 新的數(shù)據(jù)類型名;
其中“已有的數(shù)據(jù)類型”是指上面所介紹的C語(yǔ)言中所有的數(shù)據(jù)類型,包括結(jié)構(gòu)、指針和數(shù)組等,“新的數(shù)據(jù)類型名”可按用戶自己的習(xí)慣或根據(jù)任務(wù)需要決定。關(guān)鍵字typedef的作用只是將C語(yǔ)言中已有的數(shù)據(jù)類型作了置換,因此可用置換后的新數(shù)據(jù)類型名來(lái)進(jìn)行變量的定義。例如:
typedef int word; /* 定義word為新的整型數(shù)據(jù)類型名 */ word i,j; /* 將I,j定義為int型變量 */
在這個(gè)例子中,先用關(guān)鍵字typedef將word定義為新的整型數(shù)據(jù)類型,定義的過(guò)程實(shí)際上是用word置換了int,因此下面就可以直接用word對(duì)變量i,j進(jìn)行定義,而此時(shí)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為一個(gè)新的數(shù)據(jù)類型(結(jié)構(gòu)類型)名,可以直接用它來(lái)定義變量:
DATE birthday; /* 定義birthday為結(jié)構(gòu)類型變量 */ DATE * point; /* 定義指向這個(gè)結(jié)構(gòu)類型數(shù)據(jù)的指針 */
關(guān)于結(jié)構(gòu)類型數(shù)據(jù)在本章后面還要詳細(xì)討論。一般而言,用typedef定義的新數(shù)據(jù)類型用大寫字母表示,以便與C語(yǔ)言中原有的數(shù)據(jù)類型相區(qū)別。另外還要注意,用typedef可以定義各種新的數(shù)據(jù)類型名,但不能直接用來(lái)定義變量。typedef只是對(duì)已有的數(shù)據(jù)類型作了一個(gè)名字上的置換,并沒有創(chuàng)造出一個(gè)新的數(shù)據(jù)類型,例如前面例子中的word,它只是int類型的一個(gè)新名字而已。
采用typedef來(lái)重新定義數(shù)據(jù)類型有利于程序的移植,同時(shí)還可以簡(jiǎn)化較長(zhǎng)的數(shù)據(jù)類型定義(如結(jié)構(gòu)數(shù)據(jù)類型等)。在采用多模塊程序設(shè)計(jì)時(shí),如果不同的模塊程序源文件中用到同一類型的數(shù)據(jù)時(shí)(尤其是像數(shù)組、指針、結(jié)構(gòu)、聯(lián)合等復(fù)雜數(shù)據(jù)類型),經(jīng)常用typedef將這些數(shù)據(jù)重新定義并放到一個(gè)單獨(dú)的文件中,需要時(shí)再用預(yù)處理命令#include將它們包含進(jìn)來(lái)。
2.2.5 運(yùn)算符與表達(dá)式
C語(yǔ)言對(duì)數(shù)據(jù)有很強(qiáng)的表達(dá)能力,具有十分豐富的運(yùn)算符。運(yùn)算符就是完成某種特定運(yùn)算的符號(hào),表達(dá)式則是由運(yùn)算符及運(yùn)算對(duì)象所組成的具有特定含義的一個(gè)式子。C語(yǔ)言是一種表達(dá)式語(yǔ)言,在任意一個(gè)表達(dá)式的后面加一個(gè)分號(hào)“;”就構(gòu)成了一個(gè)表達(dá)式語(yǔ)句。由運(yùn)算符和表達(dá)式可以組成C語(yǔ)言程序的各種語(yǔ)句。
運(yùn)算符按其在表達(dá)式中所起的作用,可分為賦值運(yùn)算符、算術(shù)運(yùn)算符、增量與減量運(yùn)算符、關(guān)系運(yùn)算符、邏輯運(yùn)算符、位運(yùn)算符、復(fù)合賦值運(yùn)算符、逗號(hào)運(yùn)算符、條件運(yùn)算符、指針和地址運(yùn)算符、強(qiáng)制類型轉(zhuǎn)換運(yùn)算符和sizeof運(yùn)算符等。運(yùn)算符按其在表達(dá)式中與運(yùn)算對(duì)象的關(guān)系,又可分為單目運(yùn)算符、雙目運(yùn)算符和三目運(yùn)算符等。單目運(yùn)算符只需要有一個(gè)運(yùn)算對(duì)象,雙目運(yùn)算符要求有兩個(gè)運(yùn)算對(duì)象,三目運(yùn)算符要求有三個(gè)運(yùn)算對(duì)象。掌握各種運(yùn)算符的意義和使用規(guī)則,對(duì)于編寫正確的C語(yǔ)言程序是十分重要的。
1.賦值運(yùn)算符
在C語(yǔ)言中,符號(hào)“=”是一個(gè)特殊的運(yùn)算符,稱之為賦值運(yùn)算符。賦值運(yùn)算符的作用是將一個(gè)數(shù)據(jù)的值賦給一個(gè)變量,利用賦值運(yùn)算符將一個(gè)變量與一個(gè)表達(dá)式連接起來(lái)的式子稱為賦值表達(dá)式,在賦值表達(dá)式的后面加一個(gè)分號(hào)“;”便構(gòu)成了賦值語(yǔ)句。賦值語(yǔ)句的格式如下:
變量 = 表達(dá)式;
該語(yǔ)句的意義是先計(jì)算出右邊表達(dá)式的值,然后將該值賦給左邊的變量。上式中的“表達(dá)式”還可以是另一個(gè)賦值表達(dá)式,即C語(yǔ)言允許進(jìn)行多重賦值。例如:
x=9; /* 將常數(shù) 9 賦給變量x */ x=y=8; /* 將常數(shù) 8 同時(shí)賦給變量x和y */
這些都是合法的賦值語(yǔ)句。在使用賦值運(yùn)算符“=”時(shí)應(yīng)注意不要與關(guān)系運(yùn)算符“= =”相混淆,運(yùn)算符“= =”用來(lái)進(jìn)行相等關(guān)系運(yùn)算。
2.算術(shù)運(yùn)算符
C語(yǔ)言中的算術(shù)運(yùn)算符有:
+ 加或取正值運(yùn)算符
- 減或取負(fù)值運(yùn)算符
* 乘運(yùn)算符
/ 除運(yùn)算符
% 取余運(yùn)算符
上面這些運(yùn)算符中加、減、乘、除為雙目運(yùn)算符,它們要求有兩個(gè)運(yùn)算對(duì)象。對(duì)于加、減和乘法符合一般的算術(shù)運(yùn)算規(guī)則。除法運(yùn)算有所不同,如果是兩個(gè)整數(shù)相除,其結(jié)果為整數(shù),舍去小數(shù)部分,例如:5/3的結(jié)果為1,5/10的結(jié)果為0。如果是兩個(gè)浮點(diǎn)數(shù)相除,其結(jié)果為浮點(diǎn)數(shù),例如:5.0/10.0的結(jié)果為0.5。取余運(yùn)算要求兩個(gè)運(yùn)算對(duì)象均為整型數(shù)據(jù),例如:7%4的結(jié)果為3。取正值和取負(fù)值為單目運(yùn)算符,它們的運(yùn)算對(duì)象只有一個(gè),分別是取運(yùn)算對(duì)象的正值和負(fù)值。
用算術(shù)運(yùn)算符將運(yùn)算對(duì)象連接起來(lái)的式子即為算術(shù)表達(dá)式。算術(shù)運(yùn)算的一般形式為:
表達(dá)式1 算術(shù)運(yùn)算符 表達(dá)式2
例如:x+y/(a-b),(a+b)*(x-y)都是合法的算術(shù)表達(dá)式。C語(yǔ)言中規(guī)定了運(yùn)算符的優(yōu)先級(jí)和結(jié)合性。在求一個(gè)表達(dá)式的值時(shí),要按運(yùn)算符的優(yōu)先級(jí)別進(jìn)行。算術(shù)運(yùn)算符中取負(fù)值(-)的優(yōu)先級(jí)最高,其次是乘法(*)、除法(/)和取余(%)運(yùn)算符,加法(+)和減法(-)運(yùn)算符的優(yōu)先級(jí)最低。
需要時(shí)可在算術(shù)表達(dá)式中采用圓括號(hào)來(lái)改變運(yùn)算符的優(yōu)先級(jí),例如在計(jì)算表達(dá)式x+y/(a-b)的值時(shí),首先計(jì)算(a-b),然后再計(jì)算y/(a-b),最后計(jì)算x+y/(a-b)。如果在一個(gè)表達(dá)式中各個(gè)運(yùn)算符的優(yōu)先級(jí)別相同,則計(jì)算時(shí)按規(guī)定的結(jié)合方向進(jìn)行。例如計(jì)算表達(dá)式x+y-z的值,由于+和-優(yōu)先級(jí)別相同,計(jì)算時(shí)按“從左至右”的結(jié)合方向,先計(jì)算x+y,再計(jì)算(x+y)-z。這種“從左至右”的結(jié)合方向稱為“左結(jié)合性”,此外還有“右結(jié)合性”。
3.增量和減量運(yùn)算符
C語(yǔ)言中除了基本的加、減、乘、除運(yùn)算符之外,還提供一種特殊的運(yùn)算符:
++ 增量運(yùn)算符
-- 減量運(yùn)算符
增量和減量是C語(yǔ)言中特有的一種運(yùn)算符,它們的作用分別是對(duì)運(yùn)算對(duì)象作加1和減1運(yùn)算。例如:++i,i++,--j,j--等。
看起來(lái)++i和i++的作用都是使變量i的值加1,但是由于運(yùn)算符++所處的位置不同,使變量i加1的運(yùn)算過(guò)程也不同。++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
在這個(gè)程序例子中使用了Keil Cx51編譯器提供的輸出庫(kù)函數(shù)printf,在C語(yǔ)言程序中凡是使用了庫(kù)函數(shù)的,都必須在程序開始處將該庫(kù)函數(shù)的預(yù)定義文件包含進(jìn)來(lái),才能使程序得到正確的編譯和執(zhí)行。本程序在開始處使用了預(yù)處理命令#include將聲明庫(kù)函數(shù)printf原型的頭文件stdio.h包含到程序中去。另外,為了使庫(kù)函數(shù)printf能夠在μVision2仿真調(diào)試狀態(tài)下正確工作,應(yīng)在C語(yǔ)言源程序中增加對(duì)8051單片機(jī)串行口初始化的語(yǔ)句,或者將C語(yǔ)言源程序與修改后(加入了8051單片機(jī)串口初始化指令)的啟動(dòng)程序STARTUP.A51連接在一起。關(guān)于輸入輸出庫(kù)函數(shù)的詳細(xì)介紹請(qǐng)參見本書第9章。
4.關(guān)系運(yùn)算符
C語(yǔ)言中有6種關(guān)系運(yùn)算符:
> 大于
< 小于
>= 大于等于
<= 小于等于
= = 等于
!= 不等于
前4種關(guān)系運(yùn)算符具有相同的優(yōu)先級(jí),后兩種關(guān)系運(yùn)算符也具有相同的優(yōu)先級(jí);但前4種的優(yōu)先級(jí)高于后2種。用關(guān)系運(yùn)算符將兩個(gè)表達(dá)式連接起來(lái)即成為關(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)算符通常用來(lái)判別某個(gè)條件是否滿足,關(guān)系運(yùn)算的結(jié)果只有0和1兩種值。當(dāng)所指定的條件滿足時(shí)結(jié)果為1,條件不滿足時(shí)結(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編譯器提供的輸入庫(kù)函數(shù)scanf,與printf函數(shù)一樣,scanf也是通過(guò)8051單片機(jī)的串行口實(shí)現(xiàn)數(shù)據(jù)輸入的,它的使用方法與printf函數(shù)類似。
5.邏輯運(yùn)算符
C語(yǔ)言中有3種邏輯運(yùn)算符:
|| 邏輯或
&& 邏輯與
! 邏輯非
邏輯運(yùn)算符用來(lái)求某個(gè)條件式的邏輯值,用邏輯運(yùn)算符將關(guān)系表達(dá)式或邏輯量連接起來(lái)就是邏輯表達(dá)式。邏輯運(yùn)算的一般形式為:
邏輯與 條件式1 && 條件式2 邏輯或 條件式1 || 條件式2 邏輯非 ! 條件式
例如:x&&y,a||b,!z都是合法的邏輯表達(dá)式。
進(jìn)行邏輯與運(yùn)算時(shí),首先對(duì)條件式1進(jìn)行判斷,如果結(jié)果為真(非0值),則繼續(xù)對(duì)條件式2進(jìn)行判斷,當(dāng)結(jié)果也為真時(shí),表示邏輯運(yùn)算的結(jié)果為真(值為1);反之,如果條件式1的結(jié)果為假,則不再判斷條件式2,而直接給出邏輯運(yùn)算的結(jié)果為假(值為0)。
進(jìn)行邏輯或運(yùn)算時(shí),只要兩個(gè)條件式中有一個(gè)為真,邏輯運(yùn)算的結(jié)果便為真(值為1),只有當(dāng)條件式1和條件式2均不成立時(shí),邏輯運(yùn)算的結(jié)果才為假(值為0)。
進(jìn)行邏輯非運(yùn)算時(shí),對(duì)條件式的邏輯值直接取反。
邏輯運(yùn)算符的優(yōu)先級(jí)為(由高至低):!(非)→&&(與)→||(或),即邏輯非的優(yōu)先級(jí)最高。
例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)算符
能對(duì)運(yùn)算對(duì)象進(jìn)行按位操作是C語(yǔ)言的一大特點(diǎn),正是由于這一特點(diǎn)使C語(yǔ)言具有了匯編語(yǔ)言的一些功能,從而使之能對(duì)計(jì)算機(jī)的硬件直接進(jìn)行操作。C語(yǔ)言中共有6種位運(yùn)算符:
~ 按位取反
<< 左移
>> 右移
& 按位與
^ 按位異或
| 按位或
位運(yùn)算符的作用是按位對(duì)變量進(jìn)行運(yùn)算,并不改變參與運(yùn)算的變量的值。若希望按位改變運(yùn)算變量的值,則應(yīng)利用相應(yīng)的賦值運(yùn)算。另外位運(yùn)算符不能用來(lái)對(duì)浮點(diǎn)型數(shù)據(jù)進(jìn)行操作。位運(yùn)算符的優(yōu)先級(jí)從高到低依次是:按位取反(~)→左移(<<)和右移(>>)→按位與(&)→按位異或(^)→按位或(|)。位運(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)算符是用來(lái)將變量1的二進(jìn)制位值向左移動(dòng)由變量2所指定的位數(shù)。例如:a=0x8f(即二進(jìn)制數(shù)10001111),進(jìn)行左移運(yùn)算a<<2,就是將a的全部二進(jìn)制位值一起向左移動(dòng)2位,其左端移出的位值被丟棄,并在其右端補(bǔ)以相應(yīng)位數(shù)的“0”。因此,移位的結(jié)果是a=0x3c(即二進(jìn)制數(shù)(00111100)。
右移(>>)運(yùn)算符是用來(lái)將變量1的二進(jìn)制位值向右移動(dòng)由變量2指定的位數(shù)。進(jìn)行右移運(yùn)算時(shí),如果變量1屬于無(wú)符號(hào)類型數(shù)據(jù),則總是在其左端補(bǔ)“0”;如果變量1屬于有符號(hào)類型數(shù)據(jù),則在其左端補(bǔ)入原來(lái)數(shù)據(jù)的符號(hào)位(即保持原來(lái)的符號(hào)不變),其右端的移出位被丟棄。對(duì)于a= 0x8f,如果a是無(wú)符號(hào)數(shù),則執(zhí)行a>>2之后結(jié)果為a=0x23(即二進(jìn)制數(shù)00100011);如果a是有符號(hào)數(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)算首先對(duì)變量進(jìn)行某種運(yùn)算,然后將運(yùn)算的結(jié)果再賦給該變量。復(fù)合運(yùn)算的一般形式為:
變量 復(fù)合賦值運(yùn)算符 表達(dá)式
例如:a+=3等價(jià)于a=a+3;x*=y+8等價(jià)于x=x*(y+8)。凡是二目運(yùn)算符,都可以和賦值運(yùn)算符一起組合成復(fù)合賦值運(yùn)算符。采用復(fù)合賦值運(yùn)算符,可以使程序簡(jiǎn)化,同時(shí)還可以提高程序的編譯效率。
例2.6:利用復(fù)合賦值運(yùn)算符實(shí)現(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)算符實(shí)現(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.逗號(hào)運(yùn)算符
在C語(yǔ)言中逗號(hào)“,”是一個(gè)特殊的運(yùn)算符,可以用它將兩個(gè)(或多個(gè))表達(dá)式連接起來(lái),稱為逗號(hào)表達(dá)式。逗號(hào)表達(dá)式的一般形式為:
表達(dá)式1,表達(dá)式2,…,表達(dá)式n
程序運(yùn)行時(shí)對(duì)于逗號(hào)表達(dá)式的處理,是從左至右依次計(jì)算出各個(gè)表達(dá)式的值,而整個(gè)逗號(hào)表達(dá)式的值是最右邊表達(dá)式(即表達(dá)式n)的值。
例2.8:逗號(hào)運(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
在許多情況下,使用逗號(hào)表達(dá)式的目的只是為了分別得到各個(gè)表達(dá)式的值,而并不一定要得到和使用整個(gè)逗號(hào)表達(dá)式的值。另外還要注意,并不是在程序的任何地方出現(xiàn)的逗號(hào),都可以認(rèn)為是逗號(hào)運(yùn)算符。例如函數(shù)中的參數(shù)也是用逗號(hào)來(lái)間隔的,上例中庫(kù)輸出函數(shù)printf("\n%d %d %d",a,b,c)中的“a,b,c”是函數(shù)的三個(gè)參數(shù),而不是一個(gè)逗號(hào)表達(dá)式。
9.條件運(yùn)算符
條件運(yùn)算符“?:”是C語(yǔ)言中唯一的一個(gè)三目運(yùn)算符,它要求有三個(gè)運(yùn)算對(duì)象,用它可以將三個(gè)表達(dá)式連接構(gòu)成一個(gè)條件表達(dá)式。條件表達(dá)式的一般形式如下:
邏輯表達(dá)式 ? 表達(dá)式1:表達(dá)式2
其功能是首先計(jì)算邏輯表達(dá)式,當(dāng)值為真(非0值)時(shí),將表達(dá)式1的值作為整個(gè)條件表達(dá)式的值;當(dāng)邏輯表達(dá)式的值為假(0值)時(shí),將表達(dá)式2的值作為整個(gè)條件表達(dá)式的值。例如:條件表達(dá)式max=(a>b) ? a:b的執(zhí)行結(jié)果是將a和b中較大者賦值給變量max。另外,條件表達(dá)式中邏輯表達(dá)式的類型可以與表達(dá)式1和表達(dá)式2的類型不一樣。
10.指針和地址運(yùn)算符
指針是C語(yǔ)言中的一個(gè)十分重要的概念,在C語(yǔ)言的數(shù)據(jù)類型中專門有一種指針類型。變量的指針就是該變量的地址,還可以定義一個(gè)指向某個(gè)變量的指針變量。為了表示指針變量和它所指向的變量地址之間的關(guān)系,C語(yǔ)言提供了兩個(gè)專門的運(yùn)算符:
* 取內(nèi)容
& 取地址
取內(nèi)容和取地址運(yùn)算的一般形式分別為:
變量 = * 指針變量 指針變量 = & 目標(biāo)變量
取內(nèi)容運(yùn)算的含義是將指針變量所指向的目標(biāo)變量的值賦給左邊的變量;取地址運(yùn)算的含義是將目標(biāo)變量的地址賦給左邊的變量。需要注意的是,指針變量中只能存放地址(即指針型數(shù)據(jù)),不要將一個(gè)非指針類型的數(shù)據(jù)賦值給一個(gè)指針變量。
例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ǔ)言中的圓括號(hào)“()”也可作為一種運(yùn)算符使用,這就是強(qiáng)制類型轉(zhuǎn)換運(yùn)算符,它的作用是將表達(dá)式或變量的類型強(qiáng)制轉(zhuǎn)換成為所指定的類型。在C語(yǔ)言程序中進(jìn)行算術(shù)運(yùn)算時(shí),需要注意數(shù)據(jù)類型的轉(zhuǎn)換。有兩種數(shù)據(jù)類型轉(zhuǎn)換方式,即隱式轉(zhuǎn)換和顯式轉(zhuǎn)換。隱式轉(zhuǎn)換是在對(duì)程序進(jìn)行編譯時(shí)由編譯器自動(dòng)處理的。隱式轉(zhuǎn)換遵循以下規(guī)則。
① 所有char型的操作數(shù)轉(zhuǎn)換成int型。
② 用運(yùn)算符連接的兩個(gè)操作數(shù)如果具有不同的數(shù)據(jù)類型,按以下次序進(jìn)行轉(zhuǎn)換:如果一個(gè)操作數(shù)是float類型,則另一個(gè)操作數(shù)也轉(zhuǎn)換成float類型;如果一個(gè)操作數(shù)是long類型,則另一個(gè)操作數(shù)也轉(zhuǎn)換成long類型;如果一個(gè)操作數(shù)是unsigned類型,則另一個(gè)操作數(shù)也轉(zhuǎn)換成unsigned類型。
③ 在對(duì)變量賦值時(shí)發(fā)生的隱式轉(zhuǎn)換,將賦值號(hào)“=”右邊的表達(dá)式類型轉(zhuǎn)換成賦值號(hào)左邊變量的類型。例如,把整型數(shù)賦值給字符型變量,則整型數(shù)的高8位將喪失;把浮點(diǎn)數(shù)賦值給整型變量,則小數(shù)部分將喪失。在C語(yǔ)言中只有基本數(shù)據(jù)類型(即char、int、long和float)可以進(jìn)行隱式轉(zhuǎn)換。其余的數(shù)據(jù)類型不能進(jìn)行隱式轉(zhuǎn)換,例如,我們不能把一個(gè)整型數(shù)利用隱式轉(zhuǎn)換賦值給一個(gè)指針變量,在這種情況下就必須利用強(qiáng)制類型轉(zhuǎn)換運(yùn)算符來(lái)進(jìn)行顯式轉(zhuǎn)換。強(qiáng)制類型轉(zhuǎn)換運(yùn)算符的一般使用形式為:
(類型)=表達(dá)式
顯式類型轉(zhuǎn)換在給指針變量賦值時(shí)特別有用。例如,預(yù)先在8051單片機(jī)的片外數(shù)據(jù)存儲(chǔ)器(xdata)中定義了一個(gè)字符型指針變量px,如果想給這個(gè)指針變量賦一初值0xB000,可以寫成:px=(char xdata *)0xB000;這種方法特別適合于用標(biāo)識(shí)符來(lái)存取絕對(duì)地址。
例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語(yǔ)言中提供了一種用于求取數(shù)據(jù)類型、變量以及表達(dá)式的字節(jié)數(shù)的運(yùn)算符:sizeof,該運(yùn)算符的一般使用形式為:
sizeof(表達(dá)式)或sizeof(數(shù)據(jù)類型)
應(yīng)該注意的是,sizeof是一種特殊的運(yùn)算符,不要錯(cuò)誤地認(rèn)為它是一個(gè)函數(shù)。實(shí)際上,字節(jié)數(shù)的計(jì)算在程序編譯時(shí)就完成了,而不是在程序執(zhí)行的過(guò)程中才計(jì)算出來(lái)的。
例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
前面對(duì)C語(yǔ)言中的各種運(yùn)算符分別作了介紹,此外還有三個(gè)運(yùn)算符:數(shù)組下標(biāo)運(yùn)算符“[ ]”、存取結(jié)構(gòu)或聯(lián)合中變量的運(yùn)算符“->”或“.”,它們將在第5章予以介紹。表2-7給出了這些運(yùn)算符在使用過(guò)程中的優(yōu)先級(jí)和結(jié)合性。
表2-7 運(yùn)算符的優(yōu)先級(jí)和結(jié)合性

2.3 Cx51程序的基本語(yǔ)句
2.3.1 表達(dá)式語(yǔ)句
C語(yǔ)言是一種結(jié)構(gòu)化的程序設(shè)計(jì)語(yǔ)言,它提供了十分豐富的程序控制語(yǔ)句。表達(dá)式語(yǔ)句是最基本的一種語(yǔ)句。在表達(dá)式的后邊加一個(gè)分號(hào)“;”就構(gòu)成了表達(dá)式語(yǔ)句。下面的語(yǔ)句都是合法的表達(dá)式語(yǔ)句:
a=++b*9; x=8; y=7; z=(x+y)/a; ++i;
表達(dá)式語(yǔ)句也可以僅由一個(gè)分號(hào)“;”組成,這種語(yǔ)句稱為空語(yǔ)句。空語(yǔ)句是表達(dá)式語(yǔ)句的一個(gè)特例。空語(yǔ)句在程序設(shè)計(jì)中有時(shí)是很有用的,當(dāng)程序在語(yǔ)法上需要有一個(gè)語(yǔ)句,但在語(yǔ)義上并不要求有具體的動(dòng)作時(shí),便可以采用空語(yǔ)句。空語(yǔ)句通常有以下兩種用法。
① 在程序中為有關(guān)語(yǔ)句提供標(biāo)號(hào),用以標(biāo)記程序執(zhí)行的位置。例如,采用下面的語(yǔ)句可以構(gòu)成一個(gè)循環(huán)。
repeat:; .. goto repeat ;
② 在用while語(yǔ)句構(gòu)成的循環(huán)語(yǔ)句后面加一個(gè)分號(hào),形成一個(gè)不執(zhí)行其他操作的空循環(huán)體。這種空語(yǔ)句在等待某個(gè)事件發(fā)生時(shí)特別有用。例如,下面這段程序是讀取8051單片機(jī)串行口數(shù)據(jù)的函數(shù),其中就用了一個(gè)空語(yǔ)句while (!RI);來(lái)等待單片機(jī)串行口接收結(jié)束。
#include <reg51.h> /* 插入8051單片機(jī)的預(yù)定義文件 */ char _getkey () /* 函數(shù)定義 */ { /* 函數(shù)體開始 */ char c; /* 定義變量 */ while (!RI); /* 空語(yǔ)句,等待8051單片機(jī)串行口接收結(jié)束 */ c = SBUF; /* 讀串行口內(nèi)容 */ RI = 0; /* 清除串行口接收標(biāo)志 */ return (c); /* 返回 */ } /* 函數(shù)體結(jié)束 */
采用分號(hào)“;”作為空語(yǔ)句使用時(shí),要注意與簡(jiǎn)單語(yǔ)句中有效組成部分的分號(hào)相區(qū)別。不能濫用空語(yǔ)句,以免引起程序的誤操作,甚至造成程序語(yǔ)法上的錯(cuò)誤。
2.3.2 復(fù)合語(yǔ)句
復(fù)合語(yǔ)句是由若干條語(yǔ)句組合而成的一種語(yǔ)句,它是用一個(gè)大括號(hào)“{}”將若干條語(yǔ)句組合在一起而形成的一種功能塊。復(fù)合語(yǔ)句不需要以分號(hào)“;”結(jié)束,但它內(nèi)部的各條單語(yǔ)句仍需以分號(hào)“;”結(jié)束。復(fù)合語(yǔ)句的一般形式為:
{ 局部變量定義; 語(yǔ)句1; 語(yǔ)句2; … 語(yǔ)句n; }
復(fù)合語(yǔ)句在執(zhí)行時(shí),其中的各條單語(yǔ)句依次順序執(zhí)行。整個(gè)復(fù)合語(yǔ)句在語(yǔ)法上等價(jià)于一條單語(yǔ)句,因此在C語(yǔ)言程序中可以將復(fù)合語(yǔ)句視為一條單語(yǔ)句。復(fù)合語(yǔ)句允許嵌套,即在復(fù)合語(yǔ)句內(nèi)部還可以包含別的復(fù)合語(yǔ)句。通常復(fù)合語(yǔ)句都出現(xiàn)在函數(shù)中,實(shí)際上,函數(shù)的執(zhí)行部分(即函數(shù)體)就是一個(gè)復(fù)合語(yǔ)句。復(fù)合語(yǔ)句中的單語(yǔ)句一般是可執(zhí)行語(yǔ)句,此外還可以是變量的定義語(yǔ)句(說(shuō)明變量的數(shù)據(jù)類型)。
在復(fù)合語(yǔ)句內(nèi)所定義的變量,稱為該復(fù)合語(yǔ)句中的局部變量,它僅在當(dāng)前這個(gè)復(fù)合語(yǔ)句中有效。利用復(fù)合語(yǔ)句將多條單語(yǔ)句組合在一起,以及在復(fù)合語(yǔ)句中進(jìn)行局部變量定義是C語(yǔ)言的一個(gè)重要特征。
例2.12:復(fù)合語(yǔ)句及其局部變量的使用。
#include <stdio.h> main() { /* 主函數(shù)體開始 */ int a,b,c,d; /* 定義變量a,b,c,d,它們?cè)谡麄€(gè)主函數(shù)中有效 */ a = 1; b = 2; c = 3; d = 4; printf("\nX: %d %d %d %d",a,b,c,d); { /* 復(fù)合語(yǔ)句1 */ int b,m; /* 定義局部變量b,m,它們僅在復(fù)合語(yǔ)句1 中有效 */ b = 8; m = 100; printf("\nY: %d %d %d %d | %d",a,b,c,d,m); { /* 復(fù)合語(yǔ)句2 */ int c,n; /* 定義局部變量c,n,它們僅在復(fù)合語(yǔ)句2中有效 */ c = 9; n = 150; printf("\nZ: %d %d %d %d | %d %d",a,b,c,d,m,n); } /* 復(fù)合語(yǔ)句2 結(jié)束 */ printf("\nY: %d %d %d %d | %d",a,b,c,d,m); } /* 復(fù)合語(yǔ)句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
在這個(gè)程序的主函數(shù)體開始處,定義了變量a,b,c,d,它們?cè)谡麄€(gè)主函數(shù)體中都是有效的,在主函數(shù)體中的復(fù)合語(yǔ)句1和復(fù)合語(yǔ)句2中都可以使用它們。另外,在復(fù)合語(yǔ)句1中又定義了局部變量m和與主函數(shù)體中定義的變量同名的局部變量b,這種局部變量m和b僅在定義它的復(fù)合語(yǔ)句1中有效,而且局部變量b的優(yōu)先級(jí)高于在主函數(shù)體中定義的同名變量b。因此在復(fù)合語(yǔ)句1中執(zhí)行printf函數(shù)輸出的b值為8,而不是2。
同樣,在復(fù)合語(yǔ)句2中定義了一個(gè)與主函數(shù)體中同名的局部變量c,在復(fù)合語(yǔ)句2中執(zhí)行printf函數(shù)輸出的c值為9,而不是3。一旦出了復(fù)合語(yǔ)句,則其中的局部變量立即失效。如果有同名的局部變量,則恢復(fù)該變量在上一層位置所定義的初值。讀者可以通過(guò)仔細(xì)分析本程序的執(zhí)行結(jié)果來(lái)弄清各個(gè)變量的作用范圍。
2.3.3 條件語(yǔ)句
條件語(yǔ)句又稱為分支語(yǔ)句,它是用關(guān)鍵字if構(gòu)成的。C語(yǔ)言提供了三種形式的條件語(yǔ)句。
(1)if(條件表達(dá)式)語(yǔ)句
其含義為:若條件表達(dá)式的結(jié)果為真(非0值),就執(zhí)行后面的語(yǔ)句;反之若條件表達(dá)式的結(jié)果為假(0值),就不執(zhí)行后面的語(yǔ)句。這里的語(yǔ)句也可以是復(fù)合語(yǔ)句。這種條件語(yǔ)句的執(zhí)行過(guò)程如圖2.1(a)所示。
(2)if(條件表達(dá)式)語(yǔ)句1
else語(yǔ)句2
其含義為:若條件表達(dá)式的結(jié)果為真(非0值),就執(zhí)行語(yǔ)句1;反之若條件表達(dá)式的結(jié)果為假(0值),就執(zhí)行語(yǔ)句2。這里的語(yǔ)句1和語(yǔ)句2均可以是復(fù)合語(yǔ)句。這種條件語(yǔ)句的執(zhí)行過(guò)程如圖2.1(b)所示。

圖2.1 條件語(yǔ)句的執(zhí)行過(guò)程
(3)if(條件表達(dá)式1)語(yǔ)句1
else if(條件式表達(dá)2)語(yǔ)句2
else if(條件式表達(dá)3)語(yǔ)句3
… …
else if(條件表達(dá)式n)語(yǔ)句m
else語(yǔ)句n
這種條件語(yǔ)句常用來(lái)實(shí)現(xiàn)多方向條件分支,其執(zhí)行過(guò)程如圖2.2所示。

圖2.2 多分支條件語(yǔ)句的執(zhí)行過(guò)程
例2.13:條件語(yǔ)句的使用——求一元二次方程的根。
#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
在這個(gè)程序中使用了庫(kù)函數(shù)sqrt(r)來(lái)求方程的根,sqrt是一個(gè)算術(shù)庫(kù)函數(shù)。為了使程序能得到正確的編譯和執(zhí)行,在本程序的開始處使用了預(yù)處理命令#include將庫(kù)函數(shù)sqrt所在的預(yù)處理文件math.h包含到程序中去。
2.3.4 開關(guān)語(yǔ)句
開關(guān)語(yǔ)句也是一種用來(lái)實(shí)現(xiàn)多方向條件分支的語(yǔ)句。雖然采用條件語(yǔ)句也可以實(shí)現(xiàn)多方向條件分支,但是當(dāng)分支較多時(shí)會(huì)使條件語(yǔ)句的嵌套層次太多,程序冗長(zhǎng),可讀性降低。開關(guān)語(yǔ)句直接處理多分支選擇,使程序結(jié)構(gòu)清晰,使用方便。開關(guān)語(yǔ)句是用關(guān)鍵字switch構(gòu)成的,它的一般形式如下:
switch (表達(dá)式) { case 常量表達(dá)式1:語(yǔ)句1 break; case 常量表達(dá)式2:語(yǔ)句2 break; … … case 常量表達(dá)式n:語(yǔ)句n break; default:語(yǔ)句d }
開關(guān)語(yǔ)句的執(zhí)行過(guò)程是:將switch后面表達(dá)式的值與case后面各個(gè)常量表達(dá)式的值逐個(gè)進(jìn)行比較,若遇到匹配時(shí),就執(zhí)行相應(yīng)case后面的語(yǔ)句,然后執(zhí)行break語(yǔ)句,break語(yǔ)句又稱間斷語(yǔ)句,它的功能是中止當(dāng)前語(yǔ)句的執(zhí)行,使程序跳出switch語(yǔ)句。若無(wú)匹配的情況,則只執(zhí)行語(yǔ)句d。開關(guān)語(yǔ)句的執(zhí)行過(guò)程如圖2.3所示。

圖2.3 開關(guān)語(yǔ)句的執(zhí)行過(guò)程
例2.14:開關(guān)語(yǔ)句的使用。
本程序按照輸入的年份year和月份month,計(jì)算該月有多少天。程序需要判斷該年是否為閏年。閏年的2月有29天,平年的2月只有28天。閏年的條件是:年份數(shù)year能被4整除,但不能被100整除;或者年份數(shù)year能被400整除。這個(gè)條件可以用一個(gè)邏輯關(guān)系式來(lái)表達(dá):
year%4= =0 && year%100 != 0 || year%400 = = 0
當(dāng)這個(gè)表達(dá)式的值為真(非0值)時(shí),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ǔ)句
實(shí)際應(yīng)用中很多地方需要用到循環(huán)控制,如需要反復(fù)進(jìn)行某種操作等,這時(shí)可以用循環(huán)語(yǔ)句來(lái)實(shí)現(xiàn)。在C語(yǔ)言程序中用來(lái)構(gòu)成循環(huán)控制的語(yǔ)句有:while語(yǔ)句、do-while語(yǔ)句、for語(yǔ)句以及goto語(yǔ)句,分述如下。
① 采用while語(yǔ)句構(gòu)成循環(huán)結(jié)構(gòu)的一般形式如下:
while(條件表達(dá)式) 語(yǔ)句;
其意義為,當(dāng)條件表達(dá)式的結(jié)果為真(非0值)時(shí),程序就重復(fù)執(zhí)行后面的語(yǔ)句,一直執(zhí)行到條件表達(dá)式的結(jié)果變?yōu)榧伲?值)時(shí)為止。這種循環(huán)結(jié)構(gòu)是先檢查條件表達(dá)式所給出的條件,再根據(jù)檢查的結(jié)果決定是否執(zhí)行后面的語(yǔ)句。如果條件表達(dá)式的結(jié)果一開始就為假,則后面的語(yǔ)句一次也不會(huì)被執(zhí)行。這里的語(yǔ)句可以是復(fù)合語(yǔ)句。圖2.4所示為while語(yǔ)句的執(zhí)行過(guò)程。

圖2.4 while語(yǔ)句的執(zhí)行過(guò)程
例2.15:使用while語(yǔ)句計(jì)算自然數(shù)1~100的累加和。
#include<stdio.h> main() { int i,s=0; i=1; while (i<=100) { /* 復(fù)合語(yǔ)句循環(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語(yǔ)句構(gòu)成循環(huán)結(jié)構(gòu)的一般形式如下:
do語(yǔ)句while(條件表達(dá)式);
這種循環(huán)結(jié)構(gòu)的特點(diǎn)是先執(zhí)行給定的循環(huán)體語(yǔ)句,然后再檢查條件表達(dá)式的結(jié)果。當(dāng)條件表達(dá)式的值為真(非0值)時(shí),則重復(fù)執(zhí)行循環(huán)體語(yǔ)句,直到條件表達(dá)式的值變?yōu)榧伲?值)時(shí)為止。因此,用do-while語(yǔ)句構(gòu)成的循環(huán)結(jié)構(gòu)在任何條件下,循環(huán)體語(yǔ)句至少會(huì)被執(zhí)行一次。圖2.5給出了這種循環(huán)結(jié)構(gòu)的流程圖。

圖2.5 do-while循環(huán)結(jié)構(gòu)的流程圖
例2.16:用do-while語(yǔ)句構(gòu)成的循環(huán)計(jì)算自然數(shù)1~100的累加和。
#include<stdio.h> main() { int i,s=0; i=1; do { /* 復(fù)合語(yǔ)句循環(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)體語(yǔ)句和判斷條件表達(dá)式的結(jié)果的順序不同。另外,用do-while語(yǔ)句構(gòu)成的循環(huán)結(jié)構(gòu)中,while(條件表達(dá)式)的后面必須有一個(gè)分號(hào),而用while語(yǔ)句構(gòu)成的循環(huán)結(jié)構(gòu)中while(條件表達(dá)式)后面是沒有分號(hào)的。這一點(diǎn)在寫程序時(shí)一定要注意。
③ 采用for語(yǔ)句構(gòu)成循環(huán)結(jié)構(gòu)的一般形式如下:
for([初值設(shè)定表達(dá)式];[循環(huán)條件表達(dá)式];[更新表達(dá)式])語(yǔ)句
for語(yǔ)句的執(zhí)行過(guò)程是:先計(jì)算出初值設(shè)定表達(dá)式的值,以此作為循環(huán)控制變量的初值,再檢查循環(huán)條件表達(dá)式的結(jié)果,當(dāng)滿足條件時(shí)就執(zhí)行循環(huán)體語(yǔ)句并計(jì)算更新表達(dá)式,然后再根據(jù)更新表達(dá)式的計(jì)算結(jié)果來(lái)判斷循環(huán)條件是否滿足,一直進(jìn)行到循環(huán)條件表達(dá)式的結(jié)果為假(0值)時(shí)退出循環(huán)體。for語(yǔ)句的執(zhí)行過(guò)程如圖2.6所示。

圖2.6 for語(yǔ)句的執(zhí)行過(guò)程
例2.17:用for語(yǔ)句構(gòu)成的循環(huán)計(jì)算自然數(shù)1~100的累加和。
#include<stdio.h> main() { int i,s=0; for (i=1; i<=100; i++) s=s+i; /* 循環(huán)體語(yǔ)句 */ printf("1+2+ … +100 = %d\n",s); while(1); }
程序執(zhí)行結(jié)果:
1+2+…+100 = 5050
在C語(yǔ)言程序的循環(huán)結(jié)構(gòu)中,for語(yǔ)句的使用最為靈活,它不僅可以用于循環(huán)次數(shù)已經(jīng)確定的情況,而且可以用于循環(huán)次數(shù)不確定而只給出循環(huán)結(jié)束條件的情況。另外,for語(yǔ)句中的三個(gè)表達(dá)式是相互獨(dú)立的,并不一定要求三個(gè)表達(dá)式之間有依賴關(guān)系。并且for語(yǔ)句中的三個(gè)表達(dá)式都可能缺省,但無(wú)論缺省哪一個(gè)表達(dá)式,其中的兩個(gè)分號(hào)都不能缺省。一般不要缺省循環(huán)條件表達(dá)式,以免形成死循環(huán)。
例2.18:for語(yǔ)句中缺省表達(dá)式的例子——計(jì)算自然數(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)體語(yǔ)句 */ i++; /* 循環(huán)控制變量更新 */ } printf("1+2+ … +100 = %d\n",s); while(1); }
程序執(zhí)行結(jié)果:
1+2+…+100 = 5050
④ goto語(yǔ)句是一個(gè)無(wú)條件轉(zhuǎn)向語(yǔ)句,它的一般形式為:
goto語(yǔ)句標(biāo)號(hào);
其中語(yǔ)句標(biāo)號(hào)是一個(gè)帶冒號(hào)“:”的標(biāo)識(shí)符。將goto語(yǔ)句和if語(yǔ)句一起使用,可以構(gòu)成一個(gè)循環(huán)結(jié)構(gòu)。但更常見的是在C語(yǔ)言程序中采用goto語(yǔ)句來(lái)跳出多重循環(huán),需要注意的是只能用goto語(yǔ)句從內(nèi)層循環(huán)跳到外層循環(huán),而不允許從外層循環(huán)跳到內(nèi)層循環(huán)。
例2.19:使用goto語(yǔ)句跳出循環(huán)結(jié)構(gòu)。
本程序采用循環(huán)結(jié)構(gòu)來(lái)求一整數(shù)的等差數(shù)列,該數(shù)列滿足條件:頭四個(gè)數(shù)的和值為26,積值為880。該數(shù)列的公差應(yīng)為正整數(shù),否則將產(chǎn)生負(fù)的項(xiàng),此外該數(shù)列的首項(xiàng)數(shù)必須小于5,且其公差也應(yīng)小于5,否則頭四項(xiàng)的和值將大于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,…
在這個(gè)程序中采用for語(yǔ)句構(gòu)成了兩重循環(huán)嵌套,即在第一個(gè)for語(yǔ)句的循環(huán)體中又出現(xiàn)了另一個(gè)for語(yǔ)句的循環(huán)體,需要時(shí)還可以構(gòu)成多重循環(huán)結(jié)構(gòu)。程序在最內(nèi)層循環(huán)體中采用了一個(gè)goto語(yǔ)句,它的作用是直接跳出兩層循環(huán),即跳到第一層循環(huán)體外邊由標(biāo)號(hào)pt:所指出的地方。前面在介紹開關(guān)語(yǔ)句時(shí)提到采用break語(yǔ)句可以跳出開關(guān)語(yǔ)句,break語(yǔ)句還可以用于跳出循環(huán)語(yǔ)句。對(duì)于上面的例子,也可以采用break語(yǔ)句來(lái)終止循環(huán)。
例2.20:用break語(yǔ)句終止循環(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可以看到,對(duì)于多重循環(huán)的情況,break語(yǔ)句只能跳出它所處的那一層循環(huán),而不像goto語(yǔ)句可以直接從最內(nèi)層循環(huán)中跳出來(lái)。由此可見,要退出多重循環(huán)時(shí),采用goto語(yǔ)句比較方便。需要指出的是,break語(yǔ)句只能用于開關(guān)語(yǔ)句和循環(huán)語(yǔ)句之中,它是一種具有特殊功能的無(wú)條件轉(zhuǎn)移語(yǔ)句。另外還要注意,在進(jìn)行實(shí)際程序設(shè)計(jì)時(shí),為了保證程序具有良好的結(jié)構(gòu),應(yīng)當(dāng)盡可能少地采用goto語(yǔ)句,以使程序結(jié)構(gòu)清晰易讀。
在循環(huán)結(jié)構(gòu)中還可以使用一種中斷語(yǔ)句continue,它的功能是結(jié)束本次循環(huán),即跳過(guò)循環(huán)體中下面尚未執(zhí)行的語(yǔ)句,把程序流程轉(zhuǎn)移到當(dāng)前循環(huán)語(yǔ)句的下一個(gè)循環(huán)周期,并根據(jù)循環(huán)控制條件決定是否重復(fù)執(zhí)行該循環(huán)體。continue語(yǔ)句的一般形式為:
continue;
continue語(yǔ)句通常和條件語(yǔ)句一起用在由while、do-while和for語(yǔ)句構(gòu)成的循環(huán)結(jié)構(gòu)中,它也是一種具有特殊功能的無(wú)條件轉(zhuǎn)移語(yǔ)句,但與break語(yǔ)句不同,continue語(yǔ)句并不跳出循環(huán)體,而只是根據(jù)循環(huán)控制條件確定是否繼續(xù)執(zhí)行循環(huán)語(yǔ)句。
例2.21:利用continue語(yǔ)句把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 返回語(yǔ)句
返回語(yǔ)句用于終止函數(shù)的執(zhí)行,并控制程序返回到調(diào)用該函數(shù)時(shí)所處的位置。返回語(yǔ)句有兩種形式:
return(表達(dá)式); 或者return;
如果return語(yǔ)句后邊帶有表達(dá)式,則要計(jì)算表達(dá)式的值,并將表達(dá)式的值作為該函數(shù)的返回值。若使用不帶表達(dá)式的第2種形式,則被調(diào)用函數(shù)返回主調(diào)用函數(shù)時(shí),函數(shù)值不確定。一個(gè)函數(shù)的內(nèi)部可以含有多個(gè)return語(yǔ)句,但程序僅執(zhí)行其中的一個(gè)return語(yǔ)句而返回主調(diào)用函數(shù)。一個(gè)函數(shù)的內(nèi)部也可以沒有return語(yǔ)句,在這種情況下,當(dāng)程序執(zhí)行到最后一個(gè)界限符“}”處時(shí),就自動(dòng)返回主調(diào)用函數(shù)。
例2.22:return語(yǔ)句的使用。
#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
- INSTANT Mock Testing with PowerMock
- Raspberry Pi for Python Programmers Cookbook(Second Edition)
- Hyper-V 2016 Best Practices
- JavaScript+jQuery開發(fā)實(shí)戰(zhàn)
- MATLAB定量決策五大類問(wèn)題
- PhoneGap Mobile Application Development Cookbook
- 匯編語(yǔ)言編程基礎(chǔ):基于LoongArch
- Web性能實(shí)戰(zhàn)
- C++ Application Development with Code:Blocks
- Getting Started with Python
- Visual FoxPro數(shù)據(jù)庫(kù)程序設(shè)計(jì)
- Learning Perforce SCM
- Puppet Cookbook(Third Edition)
- Office VBA開發(fā)經(jīng)典:中級(jí)進(jìn)階卷
- Python量子計(jì)算實(shí)踐:基于Qiskit和IBM Quantum Experience平臺(tái)