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

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第9章C語言與程序設計補遺

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

9.2指向函數的指針變量

9.3帶參數的主函數main

9.4編譯預處理命令

9.5枚舉類型

9.6位運算

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

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

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

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

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

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

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

3.寄存器變量寄存器變量也是自動變量,它與一般自動變量的區別在于,寄存器變量的值是存儲于CPU內的寄存器中,而一般的自動變量則存儲于內存中。由于從寄存器中讀取數據要比從內存中讀取數據的速度快,所以為了提高運算速度,可以將一些頻繁使用的局部變量或形參變量定義為寄存器變量。寄存器變量只要在定義時加上存儲類別register即可。例如:registerinta;使用寄存器變量時要注意以下幾點:(1)寄存器變量本身是一個自動變量,因此只有函數內定義的變量或形參才可以定義為寄存器變量。(2)?CPU中的寄存器個數有限,所以只能將少數的變量定義為寄存器變量。(3)受寄存器長度的限制,寄存器變量只能是char、int和指針類型的變量。(4)由于寄存器變量是保存在CPU的寄存器中而不是保存在內存中,因此不能進行取地址運算。(5)在調用函數時,函數中的寄存器變量才占用寄存器存放其值,當函數調用結束時就釋放寄存器,也即寄存器變量消失。例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);}運行結果:Inputn=5↙5!=120在程序中,由于函數fac中的變量t和k頻繁使用,故將其定義為寄存器變量。4.靜態變量靜態變量的存儲空間為內存中的靜態數據區,該區域中的數據在整個程序運行期間一直占用分配給它們的存儲空間,直到整個程序結束。特別要注意的是,函數體內如果在定義靜態變量(稱為局部靜態變量)的同時進行了初始化,則以后程序不再對其進行初始化操作。這是由于第一次遇見局部靜態變量時,系統即為局部靜態變量分配了專用的內存單元并將初始化值送入這個內存單元,此后該局部靜態變量就一直使用這個內存單元,而無論函數的調用或結束;也即,下一次函數調用時,這個局部靜態變量仍然使用這個內存單元,而且并不重新初始化。這樣,局部靜態變量就可以保存前一次函數調用得到的值而用于下一次函數調用;這一點是局部靜態變量與自動變量的本質區別。

局部靜態變量的初值是在程序編譯時賦予的,在程序執行過程中不再賦值。對沒有賦初值的局部靜態變量,編譯系統自動給它賦初值0。auto型局部變量與static型局部變量的區別如表9.1所示。表9.1auto型局部變量與static型局部變量的區別例9.3分析下面程序的運行結果。#include<stdio.h>voidfun();voidmain(){ fun(); fun();}voidfun(){ staticintn=2;/*局部靜態變量*/ n++; printf("n=%d\n",n);}[解]在程序中,函數fun中定義的n為局部靜態變量;其作用域只在函數fun內。在程序執行開始前,系統已為n分配了內存單元且n的初值為2,第一次函數fun調用時,執行“n++;”后n值為3,輸出3;第一次調用fun函數結束,但系統分配給n的內存單元并不釋放。第二次調用fun時,執行“n++;”后n值由3變為4?(注意,不執行對靜態局部變量的初始化操作staticintn=2),故輸出結果為4。所以,程序執行后的輸出結果為:n=3n=4例9.4分析下面程序的運行結果。#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;}[解]程序執行的動態圖如圖9-3所示。圖9-3程序執行的動態圖

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

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

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

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

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

2.用函數指針變量調用函數定義了函數指針變量并初始化后,就可以在程序中通過函數指針變量來調用所需要的函數了。調用函數的一般形式為(*指針變量名)(實參表)由于優先級不同,所以“*指針變量名”必須用圓括號“()”括起來,表示間接調用指針變量所指向的函數,而后面的圓括號“()”中的內容為傳遞給被調函數的實參。例9.5用函數指針調用函數的方式實現在兩數中找出最大數的程序。#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);}運行結果:Inputtwonumbers:88,66↙max=88從程序中可以看出,用函數指針變量形式調用函數的步驟如下:(1)先定義函數指針變量;如程序中的“int(*p)(int,int);語句”。(2)將被調函數的入口地址(即函數名)賦給函數指針變量;如程序中的“p=max;”語句。(3)用函數指針變量形式來調用函數;如程序中的“z=(*p)(x,y);”語句。例9.6分析下面程序運行的結果。#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;}

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

使用函數指針變量還應注意以下幾點:(1)函數指針變量不能進行算術運算,這與數組指針變量不同;數組指針變量加減一個整數可以使指針移向后面或前面的數組元素,而函數指針的移動則毫無意義。(2)函數指針變量定義時“(*指針變量名)”的圓括號“()”不能缺省,有了括號指針變量名先和“*”結合,表示定義的變量名是一個指針變量。如果缺省了圓括號“()”,即如下面所示:int*p(int,int);則表示定義的p是一個函數;p為函數名,其前面的“*”表示函數p是返回指針值的函數,也即意思和功能完全不同了。(3)要注意函數指針變量與指向二維數組的指針變量之間的區別。如:“int(*p)(int);”和“int(*p)[4];”,前者是函數指針變量,后者是指向二維數組的指針變量。3.函數的指針變量作為函數的參數函數的形參可以是各種類型的變量,也可以是指向函數的指針變量。形參是指向函數的指針變量時可以接受實參傳來的不同函數,這種參數傳遞不是傳遞任何數據或普通變量的地址,而是傳遞函數的入口地址。當函數參數在兩個函數之間傳遞時,調用函數的實參應該是被調函數的函數名,而被調函數的形參應該是接受函數地址的函數指針變量。例9.7任意輸入兩個整數,求它們的和、差、積、商。[解]程序設計如下:#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;}運行結果:Inputa,b=12,4↙12+4=1612-4=812*4=4812/4=3程序中的add、sub、mul和div是已經定義過的函數,函數fun中的形參p是函數指針變量,主函數main調用fun函數:fun(add,a,b)則將函數add的入口地址傳給了函數指針變量p,而a、b值分別傳給了形參x、y;即函數fun中的語句“z=(*p)(x,y);”此時相當于語句“z=add(x,y);”,從而實現了對a與b的求和;其他函數調用也是如此。9.3帶參數的主函數main

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

由于主函數main是最先執行的,因此不可能在程序內部獲得實參值。所以,主函數main的實參值是從操作系統的命令行上獲得的。當需要運行一個可執行文件時,在DOS提示符下鍵入文件名及實參值,即可把這些實參傳給main的形參。帶參數的主函數main的調用形式如下:可執行文件名參數1參數2…參數n上面這一行字符稱為命令行,是在DOS系統提示符下鍵入的。其中,可執行文件名稱為命令名,其后的參數稱為命令行參數,命令名與各參數之間用空格分隔。例如:c:\file1ChinaBeijing由于命令名file1本身也算是一個參數,所以共有三個參數,因此argc的值取3,argv指針數組中元素的值為命令行中各字符串(參數均按字符串處理)的首地址。指針數組的大小即為參數個數;數組元素的初值由系統自動賦予。上述命令行參數在賦給帶參主函數main中的argv指針數組后示意見圖9-4。圖9-4帶參主函數main中的argv示意例如,在c盤根目錄下的文件file1.c的內容如下:#include<stdvo.n>voidmain(intargc,char*argv[]){while(argc>1){++argv;printf("%s\n",*argv);argc--;}}用VC++6.0中的工具欄Build中的Build命令先將file1.c生成可執行文件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);}經編譯鏈接后生成的可執行文件是ex.exe,若運行時輸入以下帶參數的命令行:exabcdefgh3k44則執行后輸出的結果是

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

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

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

C語言中,允許用一個標識符來表示一個字符串,稱之為宏。宏是一種編譯預處理命令,被定義為宏的標識符稱為宏名。在編譯預處理時,程序中所有出現的宏名都用宏定義中的字符串去替換,稱為宏代換或宏展開。注意,宏定義是由程序中的宏定義命令完成的,而進行宏代換的操作則是由預處理程序在編譯之前自動完成的,根據是否帶參數將宏定義分為不帶參的宏定義和帶參宏定義。1.不帶參數的宏定義不帶參數的宏定義一般形式如下:#define標識符字符串其中,define是關鍵字,它表示宏定義命令;標識符為所定義的宏名,它的寫法應符合C語言標識符的規則;字符串可以是常數、表達式及格式串等。例如:#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;替換時,將程序中出現的PI、SUM、NL和TRUE分別用對應的字符串替換。例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);}運行結果:

pi=3.141593

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

[解]在主函數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);”故輸出結果為: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);}程序運行后的輸出結果是

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

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

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

if((k+1)%10==0) printf("\n"); } printf("\n");}例9.14現有兩個C程序文件T1.c和myfun.c同在TC系統目錄(文件夾)下,其中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--]);}當編譯鏈接通過后,運行程序T1時,輸入Thank!↙求輸出的結果。[解]本題源程序相當于:#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--]);}從程序來看,主函數main只調用了一次函數fun,然后輸出一個回車換行符?′\n′。所以,程序的重點在函數fun。函數fun中有兩個while循環,第1個while語句“while((c=getchar())!=′\n′)s[n++]=c;”的作用是:

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

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

(3)枚舉常量(標識符)本身是有值的。枚舉類型定義時,每個枚舉常量(標識符)的值就確定了,即按定義時出現的順序依次為0、1、2、…;如上面枚舉類型enumweekday中,sun的值為0、mon的值為1、…。枚舉常量(標識符)的值可以輸出,但枚舉常量即標識符不能直接輸出。(4)也可以在定義時強制改變枚舉常量的值。例如:enumweekday{sun,mon=3,tue,wed,thu,fri,sat}a,b,c;則sun的值為0,而mon的值為3,其后的枚舉常量值順序加1,即tue的值為4、wed值為5、…。(5)若要將整數值賦給枚舉變量必須作強制類型轉換。例如:a=(enumweekday)0;這相當于:a=sun;(6)枚舉常量由于本身有值,所以可以比較大小,也可以作為循環控制變量。例如:if(a>mon)…或者:for(a=mon;a<=sat;a++)printf("%2d",a);2.枚舉變量的使用枚舉變量的值只能用賦值語句獲得,不能用scanf函數直接讀入枚舉常量(即標識符)。通常是先輸入一個整數,然后再通過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函數直接輸出枚舉變量的值——標識符形式的枚舉常量,枚舉變量的值通常也是通過switch語句以字符串形式輸出對應的信息。例如: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已知一個不透明的布袋中裝有紅、藍、黃、綠、紫色圓球各一個,現從中一次抓出兩個,問可能抓到的兩個球都有哪些顏色組合。[解]分析見例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");}}運行結果:1redblue2redyellow3redgreen4redpurple5blueyellow6bluegreen7bluepurple8yellowgreen9yellowpurple10greenpurple9.6位運算

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

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

溫馨提示

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

評論

0/150

提交評論