第9章C語言與程序設(shè)計補遺_第1頁
第9章C語言與程序設(shè)計補遺_第2頁
第9章C語言與程序設(shè)計補遺_第3頁
第9章C語言與程序設(shè)計補遺_第4頁
第9章C語言與程序設(shè)計補遺_第5頁
已閱讀5頁,還剩127頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第9章C語言與程序設(shè)計補遺

9.1變量的存儲類別與生命期

9.2指向函數(shù)的指針變量

9.3帶參數(shù)的主函數(shù)main

9.4編譯預(yù)處理命令

9.5枚舉類型

9.6位運算

9.1變量的存儲類別與生命期

1.生命期的概念從變量生命期(即由創(chuàng)建到撤消)來分,可以將變量分為靜態(tài)存儲變量和動態(tài)存儲變量兩類:(1)靜態(tài)存儲變量:在程序運行時固定分配存儲空間的變量。(2)動態(tài)存儲變量:在程序運行中根據(jù)需要動態(tài)分配存儲空間的變量。程序運行時對應(yīng)的內(nèi)存分配示意如圖9-1所示。圖9-1程序運行時對應(yīng)的內(nèi)存分配示意

全局變量和靜態(tài)局部變量(static變量)存放在靜態(tài)數(shù)據(jù)區(qū),程序開始執(zhí)行時給它們分配內(nèi)存單元,程序執(zhí)行結(jié)束時再釋放這些內(nèi)存單元。也即在程序的整個執(zhí)行過程中這些變量都存在(有自己的內(nèi)存單元),它們的生命期為程序的整個執(zhí)行過程。動態(tài)數(shù)據(jù)區(qū)存放自動局部變量、形參變量和用于中斷現(xiàn)場的保護數(shù)據(jù)。自動局部變量是指未加staic聲明的局部變量;形參變量是指函數(shù)的形參。在函數(shù)調(diào)用時為自動局部變量和形參變量在動態(tài)數(shù)據(jù)區(qū)分配內(nèi)存單元,當(dāng)函數(shù)執(zhí)行結(jié)束時釋放這些內(nèi)存單元。也即在函數(shù)的整個執(zhí)行過程中這些變量都存在,它們的生命期為函數(shù)的整個執(zhí)行過程。在C語言中,每個變量都有兩個屬性:數(shù)據(jù)類型和數(shù)據(jù)的存儲類別。前面各章節(jié)中,我們在定義變量時只涉及它的數(shù)據(jù)類型,其實還可以定義變量的存儲類別,它決定這個變量的存放位置(是靜態(tài)數(shù)據(jù)區(qū)還是動態(tài)數(shù)據(jù)區(qū))和生命期。

變量定義的一般形式如下:[存儲類別]類型標(biāo)識符變量名;其中,方括號“[]”中的內(nèi)容為可選項。C語言中的變量可以有4種存儲類別:自動變量、寄存器變量、靜態(tài)變量和外部變量,分別用存儲類別auto、register、static和extern。下面僅對自動變量、寄存器變量和靜態(tài)變量進行介紹。2.自動變量在函數(shù)體內(nèi)或復(fù)合語句內(nèi)定義變量時,如果沒有指定存儲類別或使用了“auto”存儲類別,則系統(tǒng)都認為所定義的變量為自動局部變量,簡稱為自動變量。此外,函數(shù)首部中的形參也是自動變量。例如:autointa=2,b;inta=2,b;

上述兩種定義方法是等價的,即都定義了a和b為自動變量。每當(dāng)進入函數(shù)體或復(fù)合語句時,系統(tǒng)在動態(tài)數(shù)據(jù)區(qū)為自動變量分配臨時內(nèi)存單元,退出時自動釋放這些內(nèi)存單元;再次進入函數(shù)或復(fù)合語句時,系統(tǒng)又為它們重新分配臨時內(nèi)存單元,退出時又自動釋放這些內(nèi)存單元。因此,釋放后自動變量的值不可能保留,這類變量的作用域及生命期只存在于定義它的函數(shù)體內(nèi)或復(fù)合語句內(nèi)。

自動變量在動態(tài)數(shù)據(jù)區(qū)分配內(nèi)存單元,并隨著程序的運行可能不斷釋放和重新分配內(nèi)存單元,也即這個內(nèi)存單元的位置是不固定的,因此自動變量中的值也會隨之改變。所以,自動變量在使用之前必須賦值,否則它的值是不確定的。此外,在不同函數(shù)中使用的同名自動變量也不會相互影響。例9.1分析下面程序的運行結(jié)果。#include<stdio.h>voidfun();voidmain(){ fun(); fun();}voidfun(){ intn=2;/*自動變量*/ n++; printf("n=%d\n",n);}

[解]在程序中,函數(shù)fun中定義的n為自動變量,其作用域只在函數(shù)fun內(nèi)。第一次調(diào)用fun時,為n分配臨時內(nèi)存單元且n的初值為2,執(zhí)行“n++;”后n值為3,因此輸出結(jié)果為3;第一次調(diào)用fun結(jié)束,此時分配給n的內(nèi)存單元被釋放。第二次調(diào)用fun時,又為n重新分配了內(nèi)存單元,函數(shù)fun的執(zhí)行過程與第一次一樣,因此輸出的結(jié)果仍是3。程序執(zhí)行的動態(tài)圖如圖9-2所示。圖9-2程序執(zhí)行的動態(tài)圖程序執(zhí)行后的輸出結(jié)果為:n=3n=3

3.寄存器變量寄存器變量也是自動變量,它與一般自動變量的區(qū)別在于,寄存器變量的值是存儲于CPU內(nèi)的寄存器中,而一般的自動變量則存儲于內(nèi)存中。由于從寄存器中讀取數(shù)據(jù)要比從內(nèi)存中讀取數(shù)據(jù)的速度快,所以為了提高運算速度,可以將一些頻繁使用的局部變量或形參變量定義為寄存器變量。寄存器變量只要在定義時加上存儲類別register即可。例如:registerinta;使用寄存器變量時要注意以下幾點:(1)寄存器變量本身是一個自動變量,因此只有函數(shù)內(nèi)定義的變量或形參才可以定義為寄存器變量。(2)?CPU中的寄存器個數(shù)有限,所以只能將少數(shù)的變量定義為寄存器變量。(3)受寄存器長度的限制,寄存器變量只能是char、int和指針類型的變量。(4)由于寄存器變量是保存在CPU的寄存器中而不是保存在內(nèi)存中,因此不能進行取地址運算。(5)在調(diào)用函數(shù)時,函數(shù)中的寄存器變量才占用寄存器存放其值,當(dāng)函數(shù)調(diào)用結(jié)束時就釋放寄存器,也即寄存器變量消失。例9.2編寫求n!?的程序。[解]程序如下:#include<stdio.h>longfac(intn);voidmain(){ intn; longf; printf("Inputn=");

scanf("%d",&n); f=fac(n); printf("%d!=%ld\n",n,f);}longfac(intn){ registerlongt=1; registerintk; for(k=2;k<=n;k++) t=t*k; return(t);}運行結(jié)果:Inputn=5↙5!=120在程序中,由于函數(shù)fac中的變量t和k頻繁使用,故將其定義為寄存器變量。4.靜態(tài)變量靜態(tài)變量的存儲空間為內(nèi)存中的靜態(tài)數(shù)據(jù)區(qū),該區(qū)域中的數(shù)據(jù)在整個程序運行期間一直占用分配給它們的存儲空間,直到整個程序結(jié)束。特別要注意的是,函數(shù)體內(nèi)如果在定義靜態(tài)變量(稱為局部靜態(tài)變量)的同時進行了初始化,則以后程序不再對其進行初始化操作。這是由于第一次遇見局部靜態(tài)變量時,系統(tǒng)即為局部靜態(tài)變量分配了專用的內(nèi)存單元并將初始化值送入這個內(nèi)存單元,此后該局部靜態(tài)變量就一直使用這個內(nèi)存單元,而無論函數(shù)的調(diào)用或結(jié)束;也即,下一次函數(shù)調(diào)用時,這個局部靜態(tài)變量仍然使用這個內(nèi)存單元,而且并不重新初始化。這樣,局部靜態(tài)變量就可以保存前一次函數(shù)調(diào)用得到的值而用于下一次函數(shù)調(diào)用;這一點是局部靜態(tài)變量與自動變量的本質(zhì)區(qū)別。

局部靜態(tài)變量的初值是在程序編譯時賦予的,在程序執(zhí)行過程中不再賦值。對沒有賦初值的局部靜態(tài)變量,編譯系統(tǒng)自動給它賦初值0。auto型局部變量與static型局部變量的區(qū)別如表9.1所示。表9.1auto型局部變量與static型局部變量的區(qū)別例9.3分析下面程序的運行結(jié)果。#include<stdio.h>voidfun();voidmain(){ fun(); fun();}voidfun(){ staticintn=2;/*局部靜態(tài)變量*/ n++; printf("n=%d\n",n);}[解]在程序中,函數(shù)fun中定義的n為局部靜態(tài)變量;其作用域只在函數(shù)fun內(nèi)。在程序執(zhí)行開始前,系統(tǒng)已為n分配了內(nèi)存單元且n的初值為2,第一次函數(shù)fun調(diào)用時,執(zhí)行“n++;”后n值為3,輸出3;第一次調(diào)用fun函數(shù)結(jié)束,但系統(tǒng)分配給n的內(nèi)存單元并不釋放。第二次調(diào)用fun時,執(zhí)行“n++;”后n值由3變?yōu)??(注意,不執(zhí)行對靜態(tài)局部變量的初始化操作staticintn=2),故輸出結(jié)果為4。所以,程序執(zhí)行后的輸出結(jié)果為:n=3n=4例9.4分析下面程序的運行結(jié)果。#include<stdio.h>intfun(intx,inty);voidmain(){ intj=4,m=1,k; k=fun(j,m); printf("%d,",k); k=fun(j,m); printf("%d\n",k);}intfun(intx,inty){ staticintm=0,i=2; i=i+m+1; m=i+x+y; returnm;}[解]程序執(zhí)行的動態(tài)圖如圖9-3所示。圖9-3程序執(zhí)行的動態(tài)圖

由于靜態(tài)局部變量在每次函數(shù)調(diào)用結(jié)束時并不消失,所以在動態(tài)圖中,我們將靜態(tài)局部變量m和i放置于函數(shù)fun空間的開始處,并且當(dāng)函數(shù)fun結(jié)束時,它們?nèi)匀淮嬖?在動態(tài)圖上是用一條橫線將它們與局部自動變量分開,且這條線一直持續(xù)到程序結(jié)束)。在下一次調(diào)用函數(shù)fun時,仍可使用它們的值。由動態(tài)圖可知,程序的運行結(jié)果為:8,17。9.2指向函數(shù)的指針變量

在C語言中,一個函數(shù)所對應(yīng)的程序代碼總是存放在一段連續(xù)的內(nèi)存區(qū)域內(nèi),并且像數(shù)組名代表數(shù)組的首地址一樣,函數(shù)名就代表該函數(shù)代碼所占內(nèi)存區(qū)域的首地址。不同的函數(shù)有不同的首地址。因此,函數(shù)名可以看做為是一個廣義的變量,我們可以把這個“變量”——函數(shù)名(函數(shù)的首地址)賦給一個指針變量,使該指針變量指向這個函數(shù),然后通過指針變量就可以找到并且調(diào)用執(zhí)行這個函數(shù)。這種指向函數(shù)的指針變量就稱為“函數(shù)指針變量”。

1.函數(shù)指針變量的定義與初始化函數(shù)指針變量定義的一般形式為類型說明符(*指針變量名)();其中,類型說明符表示被指向的那個函數(shù)的返回值類型,“(*指針變量名)”表示“*”后面的變量名是一個指針變量,最后的空括號“()”表示這個指針變量的指向是一個函數(shù)。例如:int(*p)();

定義了p是一個指向函數(shù)的指針變量,該函數(shù)的返回值為整型。p是用來存放函數(shù)的入口地址的,在沒有賦值前它不指向任何一個具體函數(shù),并具有一個空指針值。想要通過函數(shù)指針變量來實現(xiàn)對某個函數(shù)的調(diào)用,還必須對函數(shù)指針變量進行初始化,即將需要調(diào)用的某個函數(shù)入口地址賦給它;由于函數(shù)名代表該函數(shù)的入口地址,因此是將某個函數(shù)名賦給函數(shù)指針變量。函數(shù)指針變量初始化的方式有兩種:一種是直接賦值;一種是加地址運算符“&”賦值。格式如下:函數(shù)指針變量名=函數(shù)名;或者函數(shù)指針變量名=&函數(shù)名;也可以在函數(shù)指針變量定義時進行初始化:類型說明符(*指針變量名)(形式參數(shù)表)=函數(shù)名;或者類型說明符(*指針變量名)(形式參數(shù)表)=&函數(shù)名;

函數(shù)的指針變量與普通指針變量都能實現(xiàn)間接訪問,其唯一的區(qū)別是:普通指針變量指向的是內(nèi)存的數(shù)據(jù)存儲區(qū),而函數(shù)的指針變量指向的是內(nèi)存的程序代碼區(qū)。因此,普通指針變量的“?*?”運算是訪問內(nèi)存中的數(shù)據(jù),而函數(shù)的指針變量執(zhí)行“?*?”運算時,其結(jié)果是使程序控制轉(zhuǎn)移到由函數(shù)指針變量所指向的函數(shù)入口地址,并開始執(zhí)行該函數(shù)。此外,形式參數(shù)表也與第5章函數(shù)中的形式參數(shù)表不同,只能給出形參的類型。

2.用函數(shù)指針變量調(diào)用函數(shù)定義了函數(shù)指針變量并初始化后,就可以在程序中通過函數(shù)指針變量來調(diào)用所需要的函數(shù)了。調(diào)用函數(shù)的一般形式為(*指針變量名)(實參表)由于優(yōu)先級不同,所以“*指針變量名”必須用圓括號“()”括起來,表示間接調(diào)用指針變量所指向的函數(shù),而后面的圓括號“()”中的內(nèi)容為傳遞給被調(diào)函數(shù)的實參。例9.5用函數(shù)指針調(diào)用函數(shù)的方式實現(xiàn)在兩數(shù)中找出最大數(shù)的程序。#include<stdio.h>intmax(inta,intb);voidmain(){ int(*p)(int,int); intx,y,z; p=max; printf("Inputtwonumbers:\n");

scanf("%d,%d",&x,&y); z=(*p)(x,y);

printf("max=%d\n",z);}intmax(inta,intb){ if(a>b) return(a); else return(b);}運行結(jié)果:Inputtwonumbers:88,66↙max=88從程序中可以看出,用函數(shù)指針變量形式調(diào)用函數(shù)的步驟如下:(1)先定義函數(shù)指針變量;如程序中的“int(*p)(int,int);語句”。(2)將被調(diào)函數(shù)的入口地址(即函數(shù)名)賦給函數(shù)指針變量;如程序中的“p=max;”語句。(3)用函數(shù)指針變量形式來調(diào)用函數(shù);如程序中的“z=(*p)(x,y);”語句。例9.6分析下面程序運行的結(jié)果。#include<stdio.h>floatf1(floatn);floatf2(floatn);voidmain(){ float(*p1)(float),(*p2)(float),(*t)(float),y1,y2;

p1=f1; p2=f2; y1=p2(p1(2.0)); t=p1;p1=p2;p2=t; y2=p2(p1(2.0)); printf("%3.0f,%3.0f\n",y1,y2);}floatf1(floatn){ returnn*n;}floatf2(floatn){ return2*n;}

[解]程序中,函數(shù)f1實現(xiàn)的是返回參數(shù)值的平方值,函數(shù)f2實現(xiàn)的是返回參數(shù)值的2倍。在主函數(shù)main中定義了三個函數(shù)指針變量p1、p2和t,語句“p1=f1;p2=f2;”讓函數(shù)指針變量p1指向函數(shù)f1,函數(shù)指針變量p2指向函數(shù)f2,然后調(diào)用“p2(p1(2.0))”,即先讓2.0平方后再乘以2,即結(jié)果為8并賦給變量y1。接下來,語句“t=p1;p1=p2;p2=t;”交換了p1和p2的指向,即此時p1指向f2,p2指向f1。再次調(diào)用“p2(p1(2.0))”,則是先讓2.0乘以2然后再平方,即結(jié)果為16并賦給y2。因此,最后輸出的y1和y2值為:8,16。

使用函數(shù)指針變量還應(yīng)注意以下幾點:(1)函數(shù)指針變量不能進行算術(shù)運算,這與數(shù)組指針變量不同;數(shù)組指針變量加減一個整數(shù)可以使指針移向后面或前面的數(shù)組元素,而函數(shù)指針的移動則毫無意義。(2)函數(shù)指針變量定義時“(*指針變量名)”的圓括號“()”不能缺省,有了括號指針變量名先和“*”結(jié)合,表示定義的變量名是一個指針變量。如果缺省了圓括號“()”,即如下面所示:int*p(int,int);則表示定義的p是一個函數(shù);p為函數(shù)名,其前面的“*”表示函數(shù)p是返回指針值的函數(shù),也即意思和功能完全不同了。(3)要注意函數(shù)指針變量與指向二維數(shù)組的指針變量之間的區(qū)別。如:“int(*p)(int);”和“int(*p)[4];”,前者是函數(shù)指針變量,后者是指向二維數(shù)組的指針變量。3.函數(shù)的指針變量作為函數(shù)的參數(shù)函數(shù)的形參可以是各種類型的變量,也可以是指向函數(shù)的指針變量。形參是指向函數(shù)的指針變量時可以接受實參傳來的不同函數(shù),這種參數(shù)傳遞不是傳遞任何數(shù)據(jù)或普通變量的地址,而是傳遞函數(shù)的入口地址。當(dāng)函數(shù)參數(shù)在兩個函數(shù)之間傳遞時,調(diào)用函數(shù)的實參應(yīng)該是被調(diào)函數(shù)的函數(shù)名,而被調(diào)函數(shù)的形參應(yīng)該是接受函數(shù)地址的函數(shù)指針變量。例9.7任意輸入兩個整數(shù),求它們的和、差、積、商。[解]程序設(shè)計如下:#include<stdio.h>intadd(intx,inty);intsub(intx,inty);intmul(intx,inty);intdiv(intx,inty);intfun(int(*p)(int,int),intx,inty);voidmain(){ inta,b; printf("Inputa,b="); scanf("%d,%d",&a,&b); printf("%d+%d=%d\n",a,b,fun(add,a,b)); printf("%d-%d=%d\n",a,b,fun(sub,a,b));

printf("%d*%d=%d\n",a,b,fun(mul,a,b)); printf("%d/%d=%d\n",a,b,fun(div,a,b));}intadd(intx,inty){ returnx+y;}intsub(intx,inty){ returnx-y;}intmul(intx,inty){ returnx*y;}intdiv(intx,inty){ returnx/y;}intfun(int(*p)(int,int),intx,inty){ intz; z=(*p)(x,y); returnz;}運行結(jié)果:Inputa,b=12,4↙12+4=1612-4=812*4=4812/4=3程序中的add、sub、mul和div是已經(jīng)定義過的函數(shù),函數(shù)fun中的形參p是函數(shù)指針變量,主函數(shù)main調(diào)用fun函數(shù):fun(add,a,b)則將函數(shù)add的入口地址傳給了函數(shù)指針變量p,而a、b值分別傳給了形參x、y;即函數(shù)fun中的語句“z=(*p)(x,y);”此時相當(dāng)于語句“z=add(x,y);”,從而實現(xiàn)了對a與b的求和;其他函數(shù)調(diào)用也是如此。9.3帶參數(shù)的主函數(shù)main

前面各章介紹的主函數(shù)main都是不帶參數(shù)的,因此main后的括號是空括號“()”。實際上主函數(shù)main是可以帶參數(shù)的,這個參數(shù)可以認為是主函數(shù)main的形參。C語言規(guī)定主函數(shù)main的參數(shù)只能有兩個:argc和argv,并且第一個形參argc必須是整型變量,第二個形參argv必須是指向字符串的指針數(shù)組。也即,主函數(shù)main的一般形式為:voidmain(intargc,char*argv[]){函數(shù)體}其中,argc稱做參數(shù)計數(shù)器,它的值是包括命令名在內(nèi)的參數(shù)個數(shù),因此其值至少為1;argv指針數(shù)組的作用是存放命令行中命令名及每個參數(shù)字符串的首地址。

由于主函數(shù)main是最先執(zhí)行的,因此不可能在程序內(nèi)部獲得實參值。所以,主函數(shù)main的實參值是從操作系統(tǒng)的命令行上獲得的。當(dāng)需要運行一個可執(zhí)行文件時,在DOS提示符下鍵入文件名及實參值,即可把這些實參傳給main的形參。帶參數(shù)的主函數(shù)main的調(diào)用形式如下:可執(zhí)行文件名參數(shù)1參數(shù)2…參數(shù)n上面這一行字符稱為命令行,是在DOS系統(tǒng)提示符下鍵入的。其中,可執(zhí)行文件名稱為命令名,其后的參數(shù)稱為命令行參數(shù),命令名與各參數(shù)之間用空格分隔。例如:c:\file1ChinaBeijing由于命令名file1本身也算是一個參數(shù),所以共有三個參數(shù),因此argc的值取3,argv指針數(shù)組中元素的值為命令行中各字符串(參數(shù)均按字符串處理)的首地址。指針數(shù)組的大小即為參數(shù)個數(shù);數(shù)組元素的初值由系統(tǒng)自動賦予。上述命令行參數(shù)在賦給帶參主函數(shù)main中的argv指針數(shù)組后示意見圖9-4。圖9-4帶參主函數(shù)main中的argv示意例如,在c盤根目錄下的文件file1.c的內(nèi)容如下:#include<stdvo.n>voidmain(intargc,char*argv[]){while(argc>1){++argv;printf("%s\n",*argv);argc--;}}用VC++6.0中的工具欄Build中的Build命令先將file1.c生成可執(zhí)行文件file1.exe(保存于Debug子目錄中),其后再將file1.exe由Debug目錄移至c盤根目錄下,然后點擊桌面上“開始”按鈕中的運行框并輸入。c:\>file1ChinaBeijing↙則輸出為:ChinaBeijing例9.8有以下程序:#include<stdio.h>#include<string.h>voidmain(intargc,char*argv[]){ inti,len=0;

for(i=1;i<argc;i=i+2) len=len+strlen(argv[i]); printf("%d\n",len);}經(jīng)編譯鏈接后生成的可執(zhí)行文件是ex.exe,若運行時輸入以下帶參數(shù)的命令行:exabcdefgh3k44則執(zhí)行后輸出的結(jié)果是

。A)?14B)?12C)?8D)?6

[解]本題的argv示意如圖9-5所示,argc的值為5。在主函數(shù)main中,for循環(huán)執(zhí)行了兩次;當(dāng)i=1時,len=0+strlen(argv[1]),argv[1]="abcd";故此時len值為4。當(dāng)i=3時,len=4+strlen(argv[3]),其中argv[3]="h3",故此時len值為6。當(dāng)i=5時,退出循環(huán)。所以最后輸出的len值為6,即應(yīng)選D項。圖9-5argv示意9.4編譯預(yù)處理命令

預(yù)處理是C語言所具有的一種對源程序處理的功能。所謂預(yù)處理,就是指在正常編譯之前對源程序進行預(yù)先處理。也即,源程序在正常編譯之前先執(zhí)行源程序中的預(yù)處理命令進行預(yù)處理,然后再編譯源程序。通常把預(yù)處理看做編譯的一部分,是編譯中最先執(zhí)行的部分。預(yù)處理功能包含了一組預(yù)處理命令,不同的預(yù)處理命令實現(xiàn)不同的功能;最常使用的是文件包含命令和宏定義,在調(diào)試程序時也可使用條件編譯命令。預(yù)處理命令都是以“#”開頭,每個預(yù)處理命令必須單獨占一行,并且未尾不加分號(這就是命令與語句的區(qū)別)。預(yù)處理命令可以出現(xiàn)在程序的任何地方,但一般都將預(yù)處理命令放在源程序的首部,其作用域從預(yù)處理命令在程序中該命令說明的位置開始到程序的結(jié)束。預(yù)處理命令參數(shù)是一種替換功能,這種替換功能只是簡單的替代,不做語法檢查;例如,宏定義就是用定義的字符串來替代宏名;又如,文件包含命令就是一個用文件內(nèi)容替代被包含的文件名;這樣的替代目的是為了使程序的書寫更加簡潔。C語言提供的預(yù)處理命令有宏定義、文件包含和條件編譯。下面只對宏定義和文件包含這兩個命令進行介紹。9.4.1宏定義命令

C語言中,允許用一個標(biāo)識符來表示一個字符串,稱之為宏。宏是一種編譯預(yù)處理命令,被定義為宏的標(biāo)識符稱為宏名。在編譯預(yù)處理時,程序中所有出現(xiàn)的宏名都用宏定義中的字符串去替換,稱為宏代換或宏展開。注意,宏定義是由程序中的宏定義命令完成的,而進行宏代換的操作則是由預(yù)處理程序在編譯之前自動完成的,根據(jù)是否帶參數(shù)將宏定義分為不帶參的宏定義和帶參宏定義。1.不帶參數(shù)的宏定義不帶參數(shù)的宏定義一般形式如下:#define標(biāo)識符字符串其中,define是關(guān)鍵字,它表示宏定義命令;標(biāo)識符為所定義的宏名,它的寫法應(yīng)符合C語言標(biāo)識符的規(guī)則;字符串可以是常數(shù)、表達式及格式串等。例如:#definePI3.14159#defineSUM10+20#defineNLprintf("\n")#defineTRUE1這里,PI、SUM、NL、TRUE都是宏名,而3.14159、10+20、printf("\n")和1都是被定義的字符串。宏定義是將PI、SUM、NL、TRUE分別定義為3.14159、10+20、printf("\n")和1;替換時,將程序中出現(xiàn)的PI、SUM、NL和TRUE分別用對應(yīng)的字符串替換。例9.9利用如下公式計算圓周率π的近似值,直到最后一項的絕對值小于ε(ε?=?10-7)。

[解]程序如下:#include<stdio.h>#include<math.h>#defineEpsilon1e-7voidmain()

{

inti,m=1,sign=1; doublepi=4,t=4;

for(i=1;fabs(t)>Epsilon;i++) { sign=-sign; m+=2; t=sign*4.0/m; pi+=t; } printf("pi=%f\n",pi);}運行結(jié)果:

pi=3.141593

2.帶參數(shù)的宏定義帶參數(shù)的宏定義的一般形式如下:#define標(biāo)識符(形參表)字符串其中,括號“()”中的形參表由一個或多個形參組成,當(dāng)形參多于一個時,形參之間用逗號隔開,對帶參數(shù)的宏展開也是用字符串替換宏名,而形參則被對應(yīng)的實參替換,其他的字符仍然保留在字符串內(nèi)。帶參宏調(diào)用的一般形式如下:宏名(實參表)例如:#defines(a,b)a*bc=s(x+y,x-y);宏調(diào)用時,用x+y替換形參a,用x-y替換形參b,其余字符不變,即“?*?”仍保留在字符串內(nèi)。經(jīng)預(yù)處理宏展開后的語句為:c=x+y*x-y;注意,定義帶參數(shù)的宏時,宏名與括號“()”之間不得有空格。帶參的宏和函數(shù)有相似之處,但它們在本質(zhì)上是不同的:(1)函數(shù)調(diào)用時先求實參表達式的值,然后將該值傳遞給對應(yīng)的形參;而帶參數(shù)的宏展開時,只是用實參字符串替換對應(yīng)的形參。(2)函數(shù)調(diào)用是在程序運行中進行的,當(dāng)調(diào)用到這個函數(shù)時才為函數(shù)的形參分配臨時內(nèi)存單元并接受實參的值;而宏展開是在編譯之前進行的,不分配內(nèi)存單元也不進行值傳遞,更沒有返回值。(3)函數(shù)中要求實參和形參都要定義數(shù)據(jù)類型,且二者的類型應(yīng)一致,否則應(yīng)進行類型轉(zhuǎn)換;而宏名沒有類型,并且所帶參數(shù)也沒有類型,展開時用指定的字符串替換宏名即可,并且宏定義時的字符串可以是任何類型的數(shù)據(jù)。(4)函數(shù)調(diào)用不會使源程序的長度發(fā)生變化,而宏展開可以使源程序的長度發(fā)生改變。(5)函數(shù)調(diào)用要占用運行時間,而宏展開是在編譯之前處理的,不占用運行時間。例9.10分析下面程序的運行結(jié)果。#include<stdio.h>#defineHDY(A,B)A/B#definePRINT(Y)printf("y=%d\n",Y)voidmain(){ inta=1,b=2,c=3,d=4,k; k=HDY(a+c,b+d); PRINT(k);}

[解]在主函數(shù)main中,“K=HDY(a+c,b+d);”語句在宏展開后為“K=a+c/b+d;”,運行后K被賦值為1+3/2+4=6,而“PRINT(K);”展開后為“printf("y=%d\n",k);”故輸出結(jié)果為:y=6。例9.11有以下程序#include<stdio.h>#defineN5#defineMN+1#definef(x)(x*M)voidmain(){

inti1,i2; i1=f(2); i2=f(1+1); printf("%d,%d\n",i1,i2);}程序運行后的輸出結(jié)果是

。A)?12,12B)?11,7C)?11,11D)?12,7

[解]宏替換只是字面上的替換,在編譯之前完成。程序中第1條要替換的語句“i1=f(2);”展開后是“i1=(2*M);”,再展開為“i1=(2*N+1);”,最后展開為“i1=(2*5+1);”,結(jié)果是i1的值為11。而第2條要替換的語句“i2=f(1+1);”,展開后是“i2=(1+1*M);”,再展開為“i2=(1+1*N+1);”,最后展開為“i2=(1+1*5+1);”,結(jié)果是i2的值為7。故應(yīng)選B。9.4.2文件包含命令

文件包含是指將指定文件中的內(nèi)容嵌入到當(dāng)前源程序文件中,文件包含命令的一般形式為:#include<文件名>或#include"文件名"其功能是:在編譯預(yù)處理時把“文件名”所指的文件內(nèi)容嵌入到當(dāng)前的源程序文件中,然后再對嵌入后的源程序文件進行編譯。對于第1種由尖括號“<>”括住文件名的文件包含命令,這種方式告訴編譯預(yù)處理程序,被包含的文件存放在C編譯系統(tǒng)所在的目錄下。該方式適用于嵌入C系統(tǒng)提供的頭文件,因為C系統(tǒng)提供的頭文件都存放在編譯系統(tǒng)所在的目錄下。對于第2種用雙引號“""”括住文件名的文件包含命令,編譯預(yù)處理程序首先到當(dāng)前文件所在的文件目錄下查找被包含的文件,如果找不到再到編譯系統(tǒng)所在的目錄下查找。當(dāng)然,也可以在文件名前給出路徑名,直接告訴編譯預(yù)處理程序被包含文件所在的確切位置。在使用文件包含命令時應(yīng)注意以下幾點:(1)?一個?#inlude命令只能包含一個文件,如果要包含多個文件,就得用多個文件包含命令。(2)文件包含可以嵌套,即在一個被包含的文件中又可以包含另一個文件。如文件1包含文件2,而文件2又要用到文件3的內(nèi)容,可在文件1中用兩個?#indule命令分別包含文件3和文件2,并且文件3的包含命令應(yīng)在文件2的包含命令之前,這樣文件1和文件2都能使用文件3的內(nèi)容。(3)被包含的文件2與其所在的文件1,經(jīng)過預(yù)處理后成為一個文件即文件1,而不是兩個文件。因此,在文件2中定義的全局變量此時在文件1中有效,即無需使用extern聲明。(4)?#include命令用于包含擴展名為?.c的源程序文件或擴展名為?.h的“頭文件”。例9.12計算xy。[解]文件filel.c中的內(nèi)容#include<stdio.h>#include"d:\c\file2.h" /*指明要包含文件file2.c的完整路徑*/intmain(){ intx,y; doublepower(int,int); /*函數(shù)聲明*/ printf("Inputx,y:"); scanf("%d,%d",&x,&y); printf("%d**%d=%f\n",x,y,power(x,y)); return0;}d盤C目錄下file2.h文件中的內(nèi)容為;doublepower(intm,intn){ inti; doubley=1.0; for(i=1;i<=n;i++) y*=m; returny;}運行結(jié)果為:Inputx,y:2,10↙2**10=1024.000000例9.13用包含排序文件的方法實現(xiàn)從高分到低分輸出學(xué)生成績。[解]文件px.c如下:voidsort(intx[],intn) /*選擇排序文件*/{ inti,j,k,t;

for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(x[k]<x[j]) k=j; if(k!=i)

{t=x[i];x[i]=x[k];x[k]=t;} }}文件cj.c如下:#include<stdio.h>#include"px.c"voidmain(){ intnum,score[50],k; printf("Inputnum="); /*輸入班級人數(shù)*/ scanf("%d",&num); printf("Inputscoreofstudent:\n"); for(k=0;k<num;k++) scanf("%d",&score[k]); /*輸入學(xué)生成績*/ sort(score,num); for(k=0;k<num;k++) { printf("%4d",score[k]); /*從高到低輸出學(xué)生的成績*/

if((k+1)%10==0) printf("\n"); } printf("\n");}例9.14現(xiàn)有兩個C程序文件T1.c和myfun.c同在TC系統(tǒng)目錄(文件夾)下,其中T1.c文件如下:#include<stdio.h>#include"myfun.c"voidmain(){ fun(); printf("\n");}myfun.c文件如下:voidfun(){ chars[20],c; intn=0; while((c=getchar())!=′\n′)

s[n++]=c; n--; while(n>=0) printf("%c",s[n--]);}當(dāng)編譯鏈接通過后,運行程序T1時,輸入Thank!↙求輸出的結(jié)果。[解]本題源程序相當(dāng)于:#include<stdio.h>voidfun();voidmain(){ fun(); printf("\n");}voidfun(){ chars[20],c; intn=0; while((c=getchar())!=′\n′) s[n++]=c; n--; while(n>=0) printf("%c",s[n--]);}從程序來看,主函數(shù)main只調(diào)用了一次函數(shù)fun,然后輸出一個回車換行符?′\n′。所以,程序的重點在函數(shù)fun。函數(shù)fun中有兩個while循環(huán),第1個while語句“while((c=getchar())!=′\n′)s[n++]=c;”的作用是:

從鍵盤上讀入字符到變量c,若讀入的不是回車換行符?′\n′,就將它存入數(shù)組s。n為數(shù)組s的下標(biāo),用來控制字符順序放入數(shù)組s中。所以當(dāng)?shù)?個while語句結(jié)束時,s數(shù)組放入了“Thank!”,而n則是下一個將要放入字符的數(shù)組元素下標(biāo)。接下來的“n--;”語句,使n退回到字符為“!”的下標(biāo)值。第2個while語句“while(n>=0)printf("%c",s[n--);”,則由數(shù)組s中的字符“!”開始由后向前(由語句“n--;”控制)直至第1個字符“T”為止,逐個字符進行輸出,所以輸出的結(jié)果即為輸入字符串的逆序:!KnahT。9.5枚舉類型

枚舉表示一類數(shù)據(jù)的個數(shù)有限,即可窮舉。枚舉類型是一類有限離散的符號數(shù)據(jù)量的集合。例如:真假、性別、星期、時辰、職稱、年級等。而實型則不是這樣,實型數(shù)據(jù)是連續(xù)的、無法窮舉的。C語言引入了枚舉類型,即在枚舉類型定義中列舉出所有可能的取值,被說明為該枚舉類型的變量只能取定義中列舉出來的值。由于C語言沒有像PASCAL語言那樣提供集合的并、交、差運算和判斷某元素是否屬于某個集合的屬于運算等,所以C語言中的枚舉類型的功能有限,在程序中很少使用。1.枚舉類型和枚舉變量的定義定義枚舉類型的一般形式為:enum枚舉類型名{枚舉常量名表};其中,enum是關(guān)鍵字,稱為枚舉類型定義標(biāo)識符;枚舉常量名表形式如下:標(biāo)識符1,標(biāo)識符2,…,標(biāo)識符n這些標(biāo)識符不得重名,它們表示的是枚舉類型定義中所有可能出現(xiàn)的枚舉值,因此是枚舉常量。例如:enumweekday{sun,mon,tue,wed,thu,fri,sat};定義了一個枚舉類型enumweekday,它有7個枚舉常量。枚舉變量如同結(jié)構(gòu)體變量和共用體變量一樣,也可以有三種定義形式:(1)先定義枚舉類型再定義枚舉變量。例如:enumweekday{sun,mon,tue,wed,thu,fri,sat};enumweekdaya,b,c;(2)在定義枚舉類型的同時定義枚舉變量。例如:enumweekday{sun,mon,tue,wed,thu,fri,sat}a,b,c;(3)直接定義枚舉變量。例如:enum{sun,mon,tue,wed,thu,fri,sat}a,b,c;使用枚舉類型時應(yīng)注意以下幾點:(1)枚舉變量的取值范圍限定在枚舉類型定義時的枚舉常量名表內(nèi),即不得出現(xiàn)枚舉常量名表以外的標(biāo)識符。(2)枚舉常量只是一個標(biāo)識符,不能與變量混淆,即不得用賦值語句給枚舉常量標(biāo)識符賦值。

(3)枚舉常量(標(biāo)識符)本身是有值的。枚舉類型定義時,每個枚舉常量(標(biāo)識符)的值就確定了,即按定義時出現(xiàn)的順序依次為0、1、2、…;如上面枚舉類型enumweekday中,sun的值為0、mon的值為1、…。枚舉常量(標(biāo)識符)的值可以輸出,但枚舉常量即標(biāo)識符不能直接輸出。(4)也可以在定義時強制改變枚舉常量的值。例如:enumweekday{sun,mon=3,tue,wed,thu,fri,sat}a,b,c;則sun的值為0,而mon的值為3,其后的枚舉常量值順序加1,即tue的值為4、wed值為5、…。(5)若要將整數(shù)值賦給枚舉變量必須作強制類型轉(zhuǎn)換。例如:a=(enumweekday)0;這相當(dāng)于:a=sun;(6)枚舉常量由于本身有值,所以可以比較大小,也可以作為循環(huán)控制變量。例如:if(a>mon)…或者:for(a=mon;a<=sat;a++)printf("%2d",a);2.枚舉變量的使用枚舉變量的值只能用賦值語句獲得,不能用scanf函數(shù)直接讀入枚舉常量(即標(biāo)識符)。通常是先輸入一個整數(shù),然后再通過switch語句給枚舉變量賦值。例如:enumweekday{sun,mon,tue,wed,thu,fri,sat}workday;scanf("%d",&n);switch(n){case0:workday=sun;case1:workday=mon;case2:workday=tue;case3:workday=wed;case4:workday=thu;case5:workday=fri;case6:workday=sat;}此外,也不能通過printf函數(shù)直接輸出枚舉變量的值——標(biāo)識符形式的枚舉常量,枚舉變量的值通常也是通過switch語句以字符串形式輸出對應(yīng)的信息。例如:swith(workday){casesun:printf("Sunday\n");casemon:printf("Monday\n");casetue:printf("Tuesday\n");casewed:printf("Wednesday\n");casethu:printf("Thursday\n");casefri:printf("Friday\n");casesat:printf("Saturday\n");}例9.15已知一個不透明的布袋中裝有紅、藍、黃、綠、紫色圓球各一個,現(xiàn)從中一次抓出兩個,問可能抓到的兩個球都有哪些顏色組合。[解]分析見例6.11。本題采用枚舉變量求解。程序如下:#include<stdio.h>enumcolor{ red,blue,yellow,green,purple};voidprint(enumcolorc);voidmain(){

ints=0,i,j;

for(i=0;i<=3;i++)for(j=i+1;j<=4;j++){if(i==j)continue;s++;printf("%5d",s);print((enumcolor)i);print((enumcolor)j);printf("\n");}}voidprint(enumcolorc){switch(c){casered:printf("red");break;caseblue:printf("blue");break;caseyellow:printf("yellow");break;casegreen:printf("green");break;casepurple:printf("purple");}}運行結(jié)果:1redblue2redyellow3redgreen4redpurple5blueyellow6bluegreen7bluepurple8yellowgreen9yellowpurple10greenpurple9.6位運算

所謂位運算,是指二進制位的運算。例如,將一個內(nèi)存單元中存儲的數(shù)據(jù)按二進制位左移或右移,兩個數(shù)按位相加等。位運算是C語言不同于其他高級語言的又一特色。C語言提供了如表9.2所示的6種位運算符。表9.2位運算符位運算符的功能是對其操作數(shù)按其二進制形式逐位進行邏輯運算或移位運算。由位運算的特點決定了操作數(shù)只能是整型或者字符型數(shù)據(jù),而不能是實型數(shù)據(jù)。1.按位與運算按位與運算符“&”是雙目運算符,其功能是參與運算的兩數(shù)各自對應(yīng)的二進制位相與,只有對應(yīng)的兩個二進制位均為1時,結(jié)果位才為1,否則為0。參與運算的數(shù)均以補碼形式出現(xiàn)。例如,9&5可寫算式如下:也即,9&5=1。按位與運算具有以下特征:(1)任何位上的二進制數(shù)只要和0進行與運算,該位即被清零。(2)任何位上的二進制數(shù)只要和1進行與運算,該位的值就保持不變。

利用這些特征可以實現(xiàn)清零、取一個數(shù)某些指定位的值或保留某些位的操作。例9.16驗證26&108的結(jié)果。[解]?26&108可寫算式如下:實現(xiàn)程序如下:#include<stdio.h>voidmain(){ inta=26,b=108,c; c=a&b; printf("a=%d\nb=%d\na&b=%d\n",a,b,c);}運行結(jié)果:a=26b=108a&b=82.按位或運算按位或運算符“?|?”是雙目運算符,其功能是參與運算的兩數(shù)各自對應(yīng)的二進制位相或,即只要對應(yīng)的兩個二進制位有一個為1時,結(jié)果位就為1。參與運算的兩個數(shù)均以補碼形式出現(xiàn)。例如

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論