函數教學課件_第1頁
函數教學課件_第2頁
函數教學課件_第3頁
函數教學課件_第4頁
已閱讀5頁,還剩106頁未讀 繼續免費閱讀

下載本文檔

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

文檔簡介

8函數1概述1函數定義的一般形式3函數的參數和函數的值 4形式參數和實際參數4函數的返回值 5函數的調用6函數調用的一般形式6函數調用的方式6被調用函數的聲明和函數原型7函數的嵌套調用8函數的遞歸調用10TOC\o"1-5"\h\z數組作為函數參數 12局部變量和全局變量17局部變量 17全局變量 19變量的存儲類別20動態存儲方式與靜態動態存儲方式20auto變量21用static聲明局部變量21register變量22用extern聲明外部變量238函數概述在前面已經介紹過,C源程序是由函數組成的。雖然在前面各章的程序中大都只有一個主函數main(),但實用程序往往由多個函數組成。函數是C源程序的基本模塊,通過對函數模塊的調用實現特定的功能。C語言中的函數相當于其它高級語言的子程序。c語言不僅提供了極為豐富的庫函數(如TurboC,MSC都提供了三百多個庫函數),還允許用戶建立自己定義的函數。用戶可把自己的算法編成一個個相對獨立的函數模塊,然后用調用的方法來使用函數。可以說C程序的全部工作都是由各式各樣的函數完成的,所以也把C語言稱為函數式語言。由于采用了函數模塊式的結構,C語言易于實現結構化程序設計。使程序的層次結構清晰,便于程序的編寫、閱讀、調試。在C語言中可從不同的角度對函數分類。.從函數定義的角度看,函數可分為庫函數和用戶定義函數兩種。1)庫函數:由C系統提供,用戶無須定義,也不必在程序中作類型說明,只需在程序前包含有該函數原型的頭文件即可在程序中直接調用。在前面各章的例題中反復用到printf、scanf、getchar、putchar、gets,puts,strcat等函數均屬此類。2)用戶定義函數:由用戶按需要寫的函數。對于用戶自定義函數,不僅要在程序中定義函數本身,而且在主調函數模塊中還必須對該被調函數進行類型說明,然后才能使用。.C語言的函數兼有其它語言中的函數和過程兩種功能,從這個角度看,又可把函數分為有返回值函數和無返回值函數兩種。1)有返回值函數:此類函數被調用執行完后將向調用者返回一個執行結果,稱為函數返回值。如數學函數即屬于此類函數。由用戶定義的這種要返回函數值的函數,必須在函數定義和函數說明中明確返回值的類型。2)無返回值函數:此類函數用于完成某項特定的處理任務,執行完成后不向調用者返回函數值。這類函數類似于其它語言的過程。由于函數無須返回值,用戶在定義此類函數時可指定它的返回為“空類型”,空類型的說明符為“void”。.從主調函數和被調函數之間數據傳送的角度看又可分為無參函數和有參函數兩種。1)無參函數:函數定義、函數說明及函數調用中均不帶參數。主調函數和被調函數之間不進行參數傳送。此類函數通常用來完成一組指定的功能,可以返回或不返回函數值。2)有參函數:也稱為帶參函數。在函數定義及函數說明時都有參數,稱為形式參數(簡稱為形參)。在函數調用時也必須給出參數,稱為實際參數(簡稱為實參)。進行函數調用時,主調函數將把實參的值傳送給形參,供被調函數使用。.C語言提供了極為豐富的庫函數,這些庫函數又可從功能角度作以下分類。1)字符類型分類函數:用于對字符按ASCII碼分類:字母,數字,控制字符,分隔符,大小寫字母等。2)轉換函數:用于字符或字符串的轉換;在字符量和各類數字量(整型,實型等)之間進行轉換;在大、小寫之間進行轉換。3)目錄路徑函數:用于文件目錄和路徑操作。4)診斷函數:用于內部錯誤檢測。5)圖形函數:用于屏幕管理和各種圖形功能。6)輸入輸出函數:用于完成輸入輸出功能。7)接口函數:用于與DOS,BIOS和硬件的接口。8)字符串函數:用于字符串操作和處理。9)內存管理函數:用于內存管理。10)數學函數:用于數學函數計算。11)日期和時間函數:用于日期,時間轉換操作。12)進程控制函數:用于進程管理和控制。13)其它函數:用于其它各種功能。以上各類函數不僅數量多,而且有的還需要硬件知識才會使用,因此要想全部掌握則需要一個較長的學習過程。應首先掌握一些最基本、最常用的函數,再逐步深入。由于課時關系,我們只介紹了很少一部分庫函數,其余部分讀者可根據需要查閱有關手冊。還應該指出的是,在C語言中,所有的函數定義,包括主函數main在內,都是平行的。也就是說,在一個函數的函數體內,不能再定義另一個函數,即不能嵌套定義。但是函數之間允許相互調用,也允許嵌套調用。習慣上把調用者稱為主調函數。函數還可以自己調用自己,稱為遞歸調用。main函數是主函數,它可以調用其它函數,而不允許被其它函數調用。因此,C程序的執行總是從main函數開始,完成對其它函數的調用后再返回到main函數,最后由main函數結束整個程序。一個C源程序必須有,也只能有一個主函數main。8.2函數定義的一般形式.無參函數的定義形式類型標識符函數名(){聲明部分語句其中類型標識符和函數名稱為函數頭。類型標識符指明了本函數的類型,函數的類型實際上是函數返回值的類型。該類型標識符與前面介紹的各種說明符相同。函數名是由用戶定義的標識符,函數名后有一個空括號,其中無參數,但括號不可少。什中的內容稱為函數體。在函數體中聲明部分,是對函數體內部所用到的變量的類型說明。在很多情況下都不要求無參函數有返回值,此時函數類型符可以寫為void。我們可以改寫一個函數定義:voidHello()printf("Hello,world\n");這里,只把main改為Hello作為函數名,其余不變。Hello函數是一個無參函數,當被其它函數調用時,輸出Helloworld字符串。.有參函數定義的一般形式類型標識符函數名(形式參數表列){聲明部分語句)有參函數比無參函數多了一個內容,即形式參數表列。在形參表中給出的參數稱為形式參數,它們可以是各種類型的變量,各參數之間用逗號間隔。在進行函數調用時,主調函數將賦予這些形式參數實際的值。形參既然是變量,必須在形參表中給出形參的類型說明。例如,定義一個函數,用于求兩個數中的大數,可寫為:intmax(inta,intb)if(a>b)returna;elsereturnb;第一行說明max函數是一個整型函數,其返回的函數值是一個整數。形參為a,b,均為整型量。a,b的具體值是由主調函數在調用時傳送過來的。在{}中的函數體內,除形參外沒有使用其它變量,因此只有語句而沒有聲明部分。在max函數體中的return語句是把a(或b)的值作為函數的值返回給主調函數。有返回值函數中至少應有一個return語句。在C程序中,一個函數的定義可以放在任意位置,既可放在主函數main之前,也可放在main之后。例如:可把max函數置在main之后,也可以把它放在main之前。修改后的程序如下所示。【例8.1]intmax(inta^intb)if(a>b)retuma;elsereturnb;main()intmax(inta,intb);intx,y,z;prints"inputtwonumbers:\n");scanf("%d%d",&x,&y);z=max(x,y);printf(,,maxmum=%d',,z);現在我們可以從函數定義、函數說明及函數調用的角度來分析整個程序,從中進一步了解函數的各種特點。程序的第1行至第5行為max函數定義。進入主函數后,因為準備調用max函數,故先對max函數進行說明(程序第8行)。函數定義和函數說明并不是一回事,在后面還要專門討論。可以看出函數說明與函數定義中的函數頭部分相同,但是末尾要加分號。程序第12行為調用max函數,并把x,y中的值傳送給max的形參a,b。max函數執行的結果(a或b)將返回給變量z。最后由主函數輸出Z的值。8.3函數的參數和函數的值形式參數和實際參數前面已經介紹過,函數的參數分為形參和實參兩種。在本小節中,進一步介紹形參、實參的特點和兩者的關系。形參出現在函數定義中,在整個函數體內都可以使用,離開該函數則不能使用。實參出現在主調函數中,進入被調函數后,實參變量也不能使用。形參和實參的功能是作數據傳送。發生函數調用時,主調函數把實參的值傳送給被調函數的形參從而實現主調函數向被調函數的數據傳送。函數的形參和實參具有以下特點:.形參變量只有在被調用時才分配內存單元,在調用結束時,即刻釋放所分配的內存單元。因此,形參只有在函數內部有效。函數調用結束返回主調函數后則不能再使用該形參變量。.實參可以是常量、變量、表達式、函數等,無論實參是何種類型的量,在進行函數調用時,它們都必須具有確定的值,以便把這些值傳送給形參。因此應預先用賦值,輸入等辦法使實參獲得確定值。.實參和形參在數量上,類型上,順序上應嚴格一致,否則會發生類型不匹配”的錯誤。.函數調用中發生的數據傳送是單向的。即只能把實參的值傳送給形參,而不能把形參的值反向地傳送給實參。因此在函數調用過程中,形參的值發生改變,而實參中的值不會變化。【例8.2】可以說明這個問題。main()intn;printf(Minputnumber\n");scanf(n%d,\&n);s(n);printf(nn=%d\n",n);)ints(intn){inti;for(i=n-l;i>=l;i—)n=n+i;printf(Mn=%d\n,,,n);本程序中定義了一個函數s,該函數的功能是求Eni的值。在主函數中輸入n值,并作為實參,在調用時傳送給s函數的形參量n(注意,本例的形參變量和實參變量的標識符都為n,但這是兩個不同的量,各自的作用域不同)。在主函數中用printf語句輸出一次n值,這bn值是實參n的值。在函數s中也用printf語句輸出了一次n值,速bn值是形參最后取得的n值0。從運行情況看,輸入n值為100。即實參n的值為100。把此值傳給函數s時,形參n的初值也為100,在執行函數過程中,形參n的值變為5050o返回主函數之后,輸出實參n的值仍為100。可見實參的值不隨形參的變化而變化。8.3.2函數的返回值函數的值是指函數被調用之后,執行函數體中的程序段所取得的并返回給主調函數的值。如調用正弦函數取得正弦值,調用例8.1的max函數取得的最大數等。對函數的值(或稱函數返回值)有以下一些說明:1)函數的值只能通過return語句返回主調函數。return語句的一般形式為:return表達式;或者為:return(表達式);該語句的功能是計算表達式的值,并返回給主調函數。在函數中允許有多個return語句,但每次調用只能有一個return語句被執行,因此只能返回一個函數值。2)函數值的類型和函數定義中函數的類型應保持一致。如果兩者不一致,則以函數類型為準,自動進行類型轉換。3)如函數值為整型,在函數定義時可以省去類型說明。4)不返回函數值的函數,可以明確定義為“空類型”,類型說明符為“void”。如例&2中函數s并不向主函數返函數值,因此可定義為:voids(intn)一旦函數被定義為空類型后,就不能在主調函數中使用被調函數的函數值了。例如,在定義S為空類型后,在主函數中寫下述語句sum=s(n);就是錯誤的。為了使程序有良好的可讀性并減少出錯,凡不要求返回值的函數都應定義為空類型。8.4函數的調用函數調用的一般形式前面已經說過,在程序中是通過對函數的調用來執行函數體的,其過程與其它語言的子程序調用相似。c語言中,函數調用的一般形式為:函數名(實際參數表)對無參函數調用時則無實際參數表。實際參數表中的參數可以是常數,變量或其它構造類型數據及表達式。各實參之間用逗號分隔。函數調用的方式在C語言中,可以用以下幾種方式調用函數:.函數表達式:函數作為表達式中的一項出現在表達式中,以函數返回值參與表達式的運算。這種方式要求函數是有返回值的。例如:z=max(x,y)是一個賦值表達式,把max的返回值賦予變量z。.函數語句:函數調用的一般形式加上分號即構成函數語句。例如:printf("%d",a);scanf("%d",&b);都是以函數語句的方式調用函數。.函數實參:函數作為另一個函數調用的實際參數出現。這種情況是把該函數的返回值作為實參進行傳送,因此要求該函數必須是有返回值的。例如:printf("%d",max(x,y));即是把max調用的返回值又作為printf函數的實參來使用的。在函數調用中還應該注意的一個問題是求值順序的問題。所謂求值順序是指對實參表中各量是自左至右使用呢,還是自右至左使用。對此,各系統的規定不一定相同。介紹printf函數時已提到過,這里從函數調用的角度再強調一下。【例8.3】main()inti=8;printf(,,%d\n%d\n%d\n%d\n'\++i,—);}如按照從右至左的順序求值。運行結果應為:8778如對printf語句中的++i,—i,i++,i-從左至右求值,結果應為:9「……右至左求值,其輸出順序都是不變的,即輸出順序總是和實參表中實參的順序相同。由于TurboC現定是自右至左求值,所以結果為8,7,7,8。上述問題如還不理解,上機一試就明白了。8.4.3 被調用函數的聲明和函數原型在主調函數中調用某函數之前應對該被調函數進行說明(聲明),這與使用變量之前要先進行變量說明是一樣的。在主調函數中對被調函數作說明的目的是使編譯系統知道被調函數返回值的類型,以便在主調函數中按此種類型時返回值作相應的處理。其-?般形式為:類型說明符被調函數名(類型形參,類型形參…);或為:類型說明符被調函數名(類型,類型…):括號內給出了形參的類型和形參名,或只給出形參類型。這便于編譯系統進行檢錯,以防止可能出現的錯誤。例8.1main函數中對max函數的說明為:intmax(inta,intb);或寫為:intmax(int,int);C語言中又規定在以下幾種情況時可以省去主調函數中對被調函數的函數說明。1)如果被調函數的返回值是整型或字符型時,可以不對被調函數作說明,而直接調用。這時系統將自動對被調函數返回值按整型處理。例8.2的主函數中未對函數s作說明而直接調用即屬此種情形。2)當被調函數的函數定義出現在主調函數之前時,在主調函數中也可以不對被調函數再作說明而直接調用。例如例8.1中,函數max的定義放在main函數之前,因此可在main函數中省去對max函數的函數說明intmax(inta,intb).3)如在所有函數定義之前,在函數外預先說明了各個函數的類型,則在以后的各主調函數中,可不再對被調函數作說明

charstr(inta);floatf(floatb);main()charstr(inta)。例如:其中第一,二行對str函數和f函數預先作了說明。因此在以后各函數中無須對str和f函數再作說明就可直接調用。4)對庫函數的調用不需要再作說明,但必須把該函數的頭文件用include命令包含在源文件前部。函數的嵌套調用C語言中不允許作嵌套的函數定義。因此各函數之間是平行的,不存在上一級函數和下一級函數的問題。但是C語言允許在一個函數的定義中出現對另一個函數的調用。這樣就出現了函數的嵌套調用。即在被調函數中又調用其它函數。這與其它語言的子程序嵌套的情形是類似的。其關系可表示如圖。圖表示了兩層嵌套的情形。其執行過程是:執行main函數中調用a函數的語句時,即轉去執行a函數,在a函數中調用b函數時,又轉去執行b函數,b函數執行完畢返回a函數的斷點繼續執行,a函數執行完畢返回main函數的斷點繼續執行。【例8.4]計算s=22!+32!本題可編寫兩個函數,一個是用來計算平方值的函數fl,另一個是用來計算階乘值的函數f2?主函數先調fl計算出平方值,再在fl中以平方值為實參,調用f2計算其階乘值,然后返回fl,再返回主函數,在循環程序中計算累加和。longfl(intp)intk;longr;longf2(int);/*函數說明*/k=p*p;Ek);returnr;)longf2(intq)|longc=l;inti;for(i=l;i<=q;i++)c=c*i;returnc;)main()(inti;longs=0;for(i=2;i<=3;i++)s=s+fl(i);printf(,'\ns=%ld\n,,,s);在程序中,函數fl和f2均為長整型,都在主函數之前定義,故不必再在主函數中對fl和f2加以說明。在主程序中,執行循環程序依次把i值作為實參調用函數fl求i2值。在fl中又發生對函數位的調用,這時是把i2的值作為實參去調f2,在f2中完成求⑵的計算。f2執行完畢把C值(即i2!)返回給fl,再由fl返回主函數實現累加。至此,由函數的嵌套調用實現了題目的要求。由于數值很大,所以函數和一些變量的類型都說明為長整型,否則會造成計算錯誤。函數的遞歸調用一個函數在它的函數體內調用它自身稱為遞歸調用。這種函數稱為遞歸函數。C語言允許函數的遞歸調用。在遞歸調用中,主調函數又是被調函數。執行遞歸函數將反復調用其自身,每調用?次就進入新的一層。例如有函數f如下:intf(intx)inty;z=f(y);returnz;這個函數是一個遞歸函數。但是運行該函數將無休止地調用其自身,這當然是不正確的。為了防止遞歸調用無終止地進行,必須在函數內有終止遞歸調用的手段。常用的辦法是加條件判斷,滿足某種條件后就不再作遞歸調用,然后逐層返回。下面舉例說明遞歸調用的執行過程。【例8.5】用遞歸法計算n!用遞歸法計算n!可用下述公式表示n!=l (n=OJ)nx(n-l)!(n>l)按公式可編程如下:longff(intn)longf;if(n<0)printf(Mn<0,inputerror");elseif(n=0lln==l)f=l;elsef=ff(n-l)*n;return(f);)main()(intn;longy;printf(fAninputainteagernumber:\nM);scanf(0%dr\&n);y=ff(n);printf(M%d!=%ldH,n,y);程序中給出的函數任是一個遞歸函數。主函數調用年后即進入函數仟執行,如果n<O,n=O或n=l時都將結束函數的執行,否則就遞歸調用ff函數自身。由于每次遞歸調用的實參為n-L即把n-1的值賦予形參n,最后當n-1的值為1時再作遞歸調用,形參n的值也為1,將使遞歸終止。然后可逐層退回。下面我們再舉例說明該過程。設執行本程序時輸入為5,R陳5!。在主函數中的調用語句即為y=ff(5),進入ff函數后,由于n=5,不等于0或1,故應執行f=ff(n-l)*n,即f=ff(5-l)*5?該語句對ff作遞歸調用即ff(4)o進行四次遞歸調用后,ff函數形參取得的值變為1,故不再繼續遞歸調用而開始逐層返回主調函數。任⑴的函數返回值為1,仟(2)的返回值為1*2=2,ff(3的返回值為2*3=6,ff(4)的返回值為6*4=24,最后返回值ff(5)為24*5=120。例8.5也可以不用遞歸的方法來完成。如可以用遞推法,即從1開始乘以2,再乘以3…直到n?遞推法比遞歸法更容易理解和實現。但是有些問題則只能用遞歸算法才能實現。典型的問題是Hanoi塔問題。【例8.6]Hanoi塔問題一塊板上有三根針,A,B,C.A針上套有64個大小不等的圓盤,大的在下,小的在上。如圖5.4所示。要把這64個圓盤從A針移動C針匕每次只能移動一個圓盤,移動可以借助B針進行。但在任何時候,任何針上的圓盤都必須保持大盤在下,小盤在上。求移動的步驟。本題算法分析如下,設A上有n個盤子。如果n=l,則將圓盤從A直接移動到C。如果n=2,則:.將A上的n-1(等于1)個圓盤移到B上;.再將A上的一個圓盤移到C上;.最后將B上的n-1(等于1)個圓盤移到C上。如果n=3,則:A.將A上的n-1(等于2,令其為n,)個圓盤移到B(借助于C),步驟如下:⑴將A上的n'-l(等于1)個圓盤移到C上。(2)將A上的一個圓盤移到B.⑶將C上的n'-l(等于1)個圓盤移到BoB.將A上的一個圓盤移到C。C.將B上的n-1(等于2,令其為n')個圓盤移到C(借助A),步驟如下:(1)將B上的n'-l(等于1)個圓盤移到A。⑵將B上的一個盤子移到C。(3)將A上的n'-l(等于1)個圓盤移到Co到此,完成了三個圓盤的移動過程。從上面分析可以看出,當n大于等于2時,移動的過程可分解為三個步驟:第一步把A上的n-1個圓盤移到B上;第二步把A上的一個圓盤移到C上;第三步把B上的n-1個圓盤移到C上;其中第一步和第三步是類同的。當n=3時,第一步和第三步又分解為類同的三步,即把n'-l個圓盤從一個針移到另一個針上,這里的n=n-l,顯然這是一個遞歸過程,據此算法可編程如下:move(intn,intx,inty,intz)if(n=l)printf("%c??>%c\n",x,z);else{move(n-l,x,z,y);printf(M%c—>%c\n*',x,z);move(n-l,y,x,z);))main(){inth;printf(H\ninputnumberAn");scanf(n%d\&h);printf("thesteptomoving%2ddiskesAn'^h);move(h/a'/bVc');從程序中可以看出,move函數是一個遞歸函數,它有四個形參n,x,y,z。n表示圓盤數,x,y,z分別表示三根針。move函數的功能是把x上的n個圓盤移動到z上。當n==l時,直接把x上的圓盤移至Z上,輸出x-z。如n!=l則分為三步:遞歸調用move讖(,把n-1個圓盤從x移至ijy;輸出xfz;遞歸調用move函數,把n-1個圓盤從y移到Zo在遞歸調用過程中n=n-l,故n的值逐次遞減,最后n=l時,終止遞歸,逐層返回。當n=4時程序運行的結果為:inputnumber:4thesteptomoving4diskes:afbb-*ca-*ba—cb—c8.7數組作為函數參數數組可以作為函數的參數使用,進行數據傳送。數組用作函數參數有兩種形式,一種是把數組元素(下標變量)作為實參使用;另一種是把數組名作為函數的形參和實參使用。.數組元素作函數實參數組元素就是下標變量,它與普通變量并無區別。因此它作為函數實參使用與普通變量是完全相同的,在發生函數調用時,把作為實參的數組元素的值傳送給形參,實現單向的值傳送。例5.4說明了這種情況。【例8.7]判別一個整數數組中各元素的值,若大于0則輸出該值,若小于等于0則輸出0值。編程如下:voidnzp(intv)if(v>0)printf(*'%dM,v);elseprintf(n%d”,0);}main()(inta[5],i;printf("input5numbers\nM);for(i=0;i<5;i+4-){scanf("%d'\&a[i]);nzp(a[i]);)本程序中首先定義一個無返回值函數nzp,并說明其形參v為整型變量。在函數體中根據v值輸出相應的結果。在main函數中用一個for語句輸入數組各元素,每輸入一個就以該元素作實參調用一次nzp函數,即把a[i]的值傳送給形參v,供nzp函數使用。.數組名作為函數參數用數組名作函數參數與用數組元素作實參有幾點不同:1)用數組元素作實參時,只要數組類型和函數的形參變量的類型一致,那么作為下標變量的數組元素的類型也和函數形參變量的類型是一致的。因此,并不要求函數的形參也是下標變量。換句話說,對數組元素的處理是按普通變量對待的。用數組名作函數參數時.,則要求形參和相對應的實參都必須是類型相同的數組,都必須有明確的數組說明。當形參和實參二者不一致時,即會發生錯誤。2)在普通變量或下標變量作函數參數時,形參變量和實參變量是由編譯系統分配的兩個不同的內存單元。在函數調用時發生的值傳送是把實參變量的值賦予形參變量。在用數組名作函數參數時,不是進行值的傳送,即不是把實參數組的每一個元素的值都賦予形參數組的各個元素。因為實際上形參數組并不存在,編譯系統不為形參數組分配內存。那么,數據的傳送是如何實現的呢?在我們曾介紹過,數組名就是數組的首地址。因此在數組名作函數參數時所進行的傳送只是地址的傳送,也就是說把實參數組的首地址賦予形參數組名。形參數組名取得該首地址之后,也就等于有了實在的數組。實際上是形參數組和實參數組為同一數組,共同擁有一段內存空間。上圖說明了這種情形。圖中設a為實參數組,類型為整型。a占有以2000為首地址的一塊內存區。b為形參數組名。當發生函數調用時,進行地址傳送,把實參數組a的苜地址傳送給形參數組名b,于是b也取得該地址2000。于是a,b兩數組共同占有以2000為首地址的一段連續內存單元。從圖中還可以看出a和b下標相同的元素實際上也占相同的兩個內存單元(整型數組每個元素占二字節)。例如a[0]和b⑼都占用2000和2001單元,當然a⑼等于b[0]o類推則有a[i]等于b[i]o【例8.8]數組a中存放了一個學生5門課程的成績,求平均成績。floataver(floata[5])inti;floatav,s=a[0];for(i=l;i<5;i++)s=s+a(i];av=s/5;returnav;voidmain()floatsco[5|,av;inti;printfC'Aninput5scores:\nM);for(i=0;iv5;i++)scanf("%f',&sco[i]);av=aver(sco);printf("averagescoreis%5.2f',av);本程序首先定義了一個實型函數aver,有一個形參為實型數組a,長度為5。在函數aver中,把各元素值相加求出平均值,返回給主函數。主函數main中首先完成數組sc。的輸入,然后以sc。作為實參調用aver函數,函數返回值送av,最后輸出av值。從運行情況可以看出,程序實現了所要求的功能。3)前面已經討論過,在變量作函數參數時,所進行的值傳送是單向的。即只能從實參傳向形參,不能從形參傳回實參。形參的初值和實參相同,而形參的值發生改變后,實參并不變化,兩者的終值是不同的。而當用數組名作函數參數時,情況則不同。由于實際上形參和實參為同?數組,因此當形參數組發生變化時,實參數組也隨之變化。當然這種情況不能理解為發生了“雙向”的值傳遞。但從實際情況來看,調用函數之后實參數組的值將由于形參數組值的變化而變化。為了說明這種情況,把例5.4改為例5.6的形式。【例8.9】題目同8.7例。改用數組名作函數參數voidnzp(inta[5])inti;printf("\nvaluesofarrayaare:\n");for(i=0;iv5;i++)if(a[i]<0)a[i]=0;printf(*'%dM,a[i]);main()intb[5],i;printf(H\ninput5numbers:\nn);for(i=0;i<5;i++)scanf("%d”,&b[i]);printf("initialvaluesofarraybare:\nH);for(i=0;i<5;i+4-)printf("%d\b[i]);nzp(b);printf(H\nlastvaluesofarraybare:\nM);for(i=0;i<5;i++)printf(n%d”,b[i]);本程序中函數nzp的形參為整數組a,長度為5。主函數中實參數組b也為整型,長度也為5。在主函數中苜先輸入數組b的值,然后輸出數組b的初始值。然后以數組名b為實參調用nzp函數。在nzp中,按要求把負值單元清0,并輸出形參數組a的值。返回主函數之后,再次輸出數組b的值。從運行結果可以看出,數組b的初值和終值是不同的,數組b的終值和數組a是相同的。這說明實參形參為同一數組,它們的值同時得以改變。用數組名作為函數參數時還應注意以下幾點:a.形參數組和實參數組的類型必須一致,否則將引起錯誤。b.形參數組和實參數組的長度可以不相同,因為在調用時,只傳送苜地址而不檢查形參數組的長度。當形參數組的長度與實參數組不一致時,雖不至于出現語法錯誤(編譯能通過),但程序執行結果將與實際不符,這是應予以注意的。【例8.10】如把例8.9修改如下:voidnzp(inta[8]){inti;printf(H\nvaluesofarrayaare:\nu);for(i=0;i<8;i4-+)(if(a[i]<0)a[i]=0;printf(M%d\a[i]);uintb[5],i;printf(n\ninput5numbers:\nM);for(i=0;iv5;i++)scanf("%d”,&b[i]);printf("initialvaluesofarraybare:\n");for(i=0;iv5;i++)printf(H%dM,b[i]);nzp(b);printf("\nlastvaluesofarraybare:\n");for(i=0;i<5;i++)printf("%d",b[i]);)本程序與例8.9程序比,nzp函數的形參數組長度改為8,函數體中,for語句的循環條件也改為i<8o因此,形參數組a和實參數組b的長度不一致。編譯能夠通過,但從結果看,數組a的元素a[5],a[6],a[7]顯然是無意義的。c.在函數形參表中,允許不給出形參數組的長度,或用一個變量來表示數組元素的個數。例如,可以寫為:voidnzp(inta[])或寫為voidnzp(intaQfintn)其中形參數組a沒有給出長度,而由n值動態地表示數組的長度。n的值由主調函數的實參進行傳送。由此,例8.10又可改為例8.11的形式【例8.11】voidnzp(inta[],intn)inti;printf("\nvaluesofarrayaare:\n");for(i=0;i<n;i++)if(a[i]<0)a[i]=0;printf(*'%dM,a[i]);main()intb[5],i;printf("\ninput5numbers:\nn);for(i=0;i<5;i++)scanf("%d”,&b[i]);printf("initialvaluesofarraybare:\nH);for(i=0;i<5;i+4-)printf("%d\b[i]);nzp(b,5);printf(H\nlastvaluesofarraybare:\nn);for(i=0;i<5;i++)printf(u%d\b[i]);本程序nzp函數形參數組a沒有給出長度,由n動態確定該長度。在main函數中,函數調用語句為nzp(b,5),其中實參5將賦予形參n作為形參數組的長度。d.多維數組也可以作為函數的參數。在函數定義時對形參數組可以指定每一維的長度,也可省去第一維的長度。因此,以下寫法都是合法的。intMA(inta[3][10])或intMA(inta[][10])o局部變量和全局變量在討論函數的形參變量時曾經提到,形參變量只在被調用期間才分配內存單元,調用結束立即釋放。這一點表明形參變量只有在函數內才是有效的,離開該函數就不能再使用了。這種變量有效性的范圍稱變量的作用域。不僅對于形參變量,C語言中所有的量都有自己的作用域。變量說明的方式不同,其作用域也不同。C語言中的變量,按作用域范圍可分為兩種,即局部變量和全局變量。局部變量局部變量也稱為內部變量。局部變量是在函數內作定義說明的。其作用域僅限于函數內,離開該函數后再使用這種變量是非法的。例如:intfl(inta)/*函數fl*/intb,c;)&b,c有效/*函數/*函數f2*/{inty,z;x,y,z有效main()intm,n;m,n有效在函數fl內定義了三個變量,a為形參,b,c為一般變量。在fl的范圍內a,b,c有效,或者說a,b,c變量的作用域限于fl內。同理,x,y,z的作用域限于f2內。m,n的作用域限于main函數內。關于局部變量的作用域還要說明以下幾點:1)主函數中定義的變量也只能在主函數中使用,不能在其它函數中使用。同時,主函數中也不能使用其它函數中定義的變量。因為主函數也是一個函數,它與其它函數是平行關系。這一點是與其它語言不同的,應予以注意。2)形參變量是屬于被調函數的局部變量,實參變量是屬于主調函數的局部變量。3)允許在不同的函數中使用相同的變量名,它們代表不同的對象,分配不同的單元,互不干擾,也不會發生混清。如在前例中,形參和實參的變量名都為n,是完全允許的。4)在復合語句中也可定義變量,其作用域只在復合語句范圍內。例如:main()ints,a;intb;s=a+b; /*b作用域*/ /*s,a作用域*/【例8.12】main()inti=2,j=3,k;k=i+j;intk=8;printf("%d\n",k);)printf("%d\n",k);本程序在main中定義了i,j,k三個變量,其中k未賦初值。而在復合語句內又定義了一個變量k,并賦初值為8。應該注意這兩個k不是同一個變量。在復合語句外由main定義的k起作用,而在復合語句內則由在更合語句內定義的k起作用。因此程序第4行的k為main所定義,其值應為5。第7行輸出k值,該行在復合語句內,由復合語句內定義的k起作用,其初值為8,故輸出值為8,第9行輸出i,k值。i是在整個程序中有效的,第7行對i賦值為3,故以輸出也為3。而第9行已在復合語句之外,輸出的k應為main所定義的k,此k值由第4行已獲得為5,故輸出也為5。全局變量全局變量也稱為外部變量,它是在函數外部定義的變量。它不屬于哪一個函數,它屬于一個源程序文件。其作用域是整個源程序。在函數中使用全局變量,一般應作全局變量說明。只有在函數內經過說明的全局變量才能使用。全局變量的說明符為externo但在一個函數之前定義的全局變量,在該函數內使用可不再加以說明。例如:inta,b;voidfl()/*外部變量*//*函數fl*/floatx,y;intfz()main()從上例可以看出a、/*外部變量*//*函數fz*//*主函數*/b、x、y都是在函數外部定義的外部變量,都是全局變量。但x,y定義在函數fl之后,而在fl內又無對x,y的說明,所以它們在fl內無效。4b定義在源程序最前面,因此在fl,f2及main內不加說明也可使用。【例&13】輸入正方體的長寬高l,w,ho求體積及三個面x*y,x*z,y*z的面積。intsl,s2,s3;intvs(inta4ntb,intc)intv;v=a*b*c;sl=a*b;s2=b*c;s3=a*c;returnv;main()intv,l,w,h;printf(H\ninputlength,widthandheight\nn);scanf(H%d%d%d,,,&l,&w,&h);v=vs(l,w,h);printf(,r\nv=%d,sl=%d,s2=%d,s3=%d\nw,v,sl,s2,s3);【例8.14]外部變量與局部變量同名。inta=3,b=5; /*a,b為外部變量*/max(inta,intb)/*a,b為外部變量*/{intc;c=a>b?a:b;retum(c);main(){inta=8;printf(M%d\nM,max(a,b));如果同一個源文件中,外部變量與局部變量同名,則在局部變量的作用范圍內,外部變量被“屏蔽”即它不起作用。變量的存儲類別動態存儲方式與靜態動態存儲方式前面已經介紹了,從變量的作用域(即從空間)角度來分,可以分為全局變量和局部變量。從另一個角度,從變量值存在的作時間(即生存期)角度來分,可以分為靜態存儲方式和動態存儲方式。靜態存儲方式:是指在程序運行期間分配固定的存儲空間的方式。動態存儲方式:是在程序運行期間根據需要進行動態的分配存儲空間的方式。用戶存儲空間可以分為三個部分:1)程序區;2)靜態存儲區;3)動態存儲區;全局變量全部存放在靜態存儲區,在程序開始執行時給全局變量分配存儲區,程序行完畢就釋放。在程序執行過程中它們占據固定的存儲單元,而不動態地進行分配和釋放;動態存儲區存放以下數據:1)函數形式參數;2)自動變量(未加static聲明的局部變量);3)函數調用實的現場保護和返回地址;對以上這些數據,在函數開始調用時分配動態

溫馨提示

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

評論

0/150

提交評論