




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第八章§8.1概述
一個較大的程序可分為若干個程序模塊,每一個模塊用來實現一個特定的功能。在高級語言中用子程序實現模塊的功能。在C中,子程序由函數來完成。一個C程序可由一個主函數和若干個其他函數構成。由主函數調用其他函數,其他函數也可以互相調用。同一個函數可以被一個或多個函數調用任意多次。函數間的調用關系#include<stdio.h>voidmain(){voidprintstar();/*對printstar函數聲明*/
voidprint_message();
/*對print_message函數聲明*/
printstar();
/*調用printstar函數*/
print_message();
/*調用print_message函數*/
printstar();
/*調用printstar函數*/
}例8.1先舉一個函數調用的簡單例子
說明:(1)一個C程序由一個或多個程序模塊組成,每一個程序模塊作為一個源程序文件。對較大的程序,一般不希望把所有內容全放在一個文件中,而是將他們分別放在若干個源文件中,再由若干源程序文件組成一個C程序。這樣便于分別編寫、分別編譯,提高調試效率。一個源程序文件可以為多個C程序公用。(2)
一個源程序文件由一個或多個函數以及其他有關內容(如命令行、數據定義等)組成。一個源程序文件是一個編譯單位,在程序編譯時是以源程序文件為單位進行編譯的,而不是以函數為單位進行編譯的。
(3)C程序的執行是從main函數開始的,如是在main函數中調用其他函數,在調用后流程返回到main函數,在main函數中結束整個程序的運行。
(4)所有函數都是平行的,即在定義函數時是分別進行的,是互相獨立的。一個函數并不從屬于另一函數,即函數不能嵌套定義。函數間可以互相調用,但不能調用main函數。main函數是系統調用的。(5)從用戶使用的角度看,函數有兩種:①
標準函數,(即庫函數)。這是由系統提供的,用戶不必自己定義這些函數,可以直接使用它們。應該說明,不同的C系統提供的庫函數的數量和功能會有一些不同,當然許多基本的函數是共同的。②用戶自己定義的函數。用以解決用戶的專門需要。
(6)從函數的形式看,函數分兩類:①無參函數。如例8.1中的printstar和print_message就是無參函數。在調用無參函數時,主調函數不向被調用函數傳遞數據。無參函數一般用來執行指定的一組操作。例如,例8.1程序中的printstar函數。②有參函數。在調用函數時,主調函數在調用被調用函數時,通過參數向被調用函數傳遞數據,一般情況下,執行被調用函數時會得到一個函數值,供主調函數使用?!?.2函數定義的一般形式
§8.2.1.無參函數的定義一般形式
定義無參函數的一般形式為:類型標識符函數名(){
聲明部分
語句部分}在定義函數時要用“類型標識符”指定函數值的類型,即函數帶回來的值的類型。例8.1中的printstar和print_message函數為void類型,表示不需要帶回函數值。
§8.2.2.有參函數定義的一般形式
定義有參函數的一般形式為:類型標識符函數名(形式參數表列){
聲明部分
語句部分}例如:intmax(intx,inty){intz;/*函數體中的聲明部分*/z=x>y?x∶y;return(z);}
§8.2.3空函數
定義空函數的一般形式為:類型標識符函數名(){}例如:dummy(){
}調用此函數時,什么工作也不做,沒有任何實際作用。在主調函數中寫上“dummy();”表明“這里要調用一個函數”,而現在這個函數沒有起作用,等以后擴充函數功能時補充上。§8.3函數參數和函數的值
§8.3.1形式參數和實際參數
在前面提到的有參函數中,在定義函數時函數名后面括弧中的變量名稱為“形式參數”(簡稱“形參”),在主調函數中調用一個函數時,函數名后面括弧中的參數(可以是一個表達式)稱為“實際參數”(簡稱“實參”)。return后面的括弧中的值()作為函數帶回的值(稱函數返回值)。
在不同的函數之間傳遞數據,可以使用的法:參數:通過形式參數和實際參數返回值:用return語句返回計算結果全局變量:外部變量大多數情況下,主調函數和被調用函數之間有數據傳遞的關系。#include<stdio.h>voidmain(){intmax(intx,inty);
/*對max函數的聲明*/inta,b,c;scanf("%d,%d",&a,&b);c=max(a,b);printf("Maxis%d",c);}
例8.2調用函數時的數據傳遞
intmax(intx,inty)/*定義有參函數max*/{intz;z=x>y?x∶y;return(z);}
運行情況如下:7,8↙Max
is
8
通過函數調用,使兩個函數中的數據發生聯系
關于形參與實參的說明:(1)在定義函數中指定的形參,在未出現函數調用時,它們并不占內存中的存儲單元。只有在發生函數調用時,函數max中的形參才被分配內存單元。在調用結束后,形參所占的內存單元也被釋放。(2)實參可以是常量、變量或表達式,如:max(3,a+b);但要求它們有確定的值。在調用時將實參的值賦給形參。(3)在被定義的函數中,必須指定形參的類型(見例8.2程序中的
“c=max(a,b);“)(4)實參與形參的類型應相同或賦值兼容。例8.2中實參和形參都是整型。如果實參為整型而形參x為實型,或者相反,則按第3章介紹的不同類型數值的賦值規則進行轉換。例如實參值a為3.5,而形參x為整型,則將實數3.5轉換成整數3,然后送到形參b。字符型與整型可以互相通用。(5)在C語言中,實參向對形參的數據傳遞是“值傳遞”,單向傳遞,只由實參傳給形參,而不能由形參傳回來給實參。在內存中,實參單元與形參單元是不同的單元。
在調用函數時,給形參分配存儲單元,并將實參對應的值傳遞給形參,調用結束后,形參單元被釋放,實參單元仍保留并維持原值。因此,在執行一個被調用函數時,形參的值如果發生改變,并不會改變主調函數的實參的值。例如,若在執行函數過程中x和y的值變為10和15,而a和b仍為2和3?!?.3.2函數的返回值通常,希望通過函數調用使主調函數能得到一個確定的值,這就是函數的返回值。例如,例8.2中,max(2,3)的值是3,max(5,2)的值是5。賦值語句將這個函數值賦給變量c。關于函數返回值的一些說明:
(1)函數的返回值是通過函數中的return語句獲得的。如果需要從被調用函數帶回一個函數值供主調函數使用,被調用函數中必須包含return語句。如果不需要從被調用函數帶回函數值可以不要return語句。
一個函數中可以有一個以上的return語句,執行到哪一個return語句,哪一個語句起作用。return語句后面的括弧也可以不要,如:“returnz;”等價于“return(z);”
return語句將被調用函數中的一個確定值帶回主調函數中去。見圖8.2中從return語句返回的箭頭。return后面的值可以是一個表達式。例如,例8.2中的函數max可以改寫成:max(intx,inty){
return(x>y?x∶y);
}(2)
函數的返回值應當屬于某一個確定的類型,在定義函數時指定函數返回值的類型。例如:下面是3個函數的首行:int
max(floatx,floaty)/*函數值為整型*/char
letter(charc1,charc2)/*函數值為字符型*/double
min(intx,inty)/*函數值為雙精度型*/在C語言中,凡不加類型說明的函數,自動按整型處理。例8.2中的max函數首行的函數類型int可以省寫,用TurboC2.0編譯程序時能通過,但用TurboC++3.0編譯程序時不能通過,因為C++要求所有函數都必須指定函數類型。因此,建議在定義時對所有函數都指定函數類型。
(3)在定義函數時指定的函數類型一般應該和return語句中的表達式類型一致。如果函數值的類型和return語句中表達式的值不一致,則以函數類型為準。對數值型數據,可以自動進行類型轉換。即函數類型決定返回值的類型。
(4)對于不帶回值的函數,應當用“void”定義函數為“無類型”(或稱“空類型”)。這樣,系統就保證不使函數帶回任何值,即禁止在調用函數中使用被調用函數的返回值。此時在函數體中不得出現return語句。
例8.3返回值類型與函數類型不同
#include<stdio.h>voidmain(){intmax(floatx,floaty);floata,b;intc;scanf("%f,%f,",&a,&b);c=max(a,b);printf("Maxis%d\n",c);}intmax(floatx,floaty){floatz;/*z為實型變量*/z=x>y?x∶y;return(z);}運行情況如下:1.5,2.5↙
Maxis2
§8.4函數的調用
§8.4.1函數調用的一般形式函數調用的一般形式為:
函數名(實參表列)如果實參表列包含多個實參,則各參數間用逗號隔開。實參與形參的個數應相等,類型應匹配。實參與形參按順序對應,一一傳遞數據。如果是調用無參函數,則“實參表列”可以沒有,但括弧不能省略。如果實參表列包括多個實參,對實參求值的順序并不是確定的,有的系統按自左至右順序求實參的值,有的系統則按自右至左順序。許多C版本是按自右而左的順序求值,例如TubroC++。例8.4實參求值的順序#include<stdio.h>voidmain(){intf(inta,intb);/*函數聲明*/inti=2,p;p=f(i,++i);/*函數調用*/printf("%d\n",p);}
intf(inta,intb)/*函數定義*/{intc;if(a>b)c=1;elseif(a==b)c=0;elsec=-1;return(c);}
如果按自右至左順序求實參的值,則函數調用相當于f(3,2)
如果按自左至右順序求實參的值,則函數調用相當于f(2,3)
對于函數調用inti=2,p;p=f(i,++i);§8.4.2函數調用的方式按函數在程序中出現的位置來分,可以有以下三種函數調用方式:1.函數語句把函數調用作為一個語句。如例8.1中的printstar(),這時不要求函數帶回值,只要求函數完成一定的操作。2.函數表達式函數出現在一個表達式中,這種表達式稱為函數表達式。這時要求函數帶回一個確定的值以參加表達式的運算。例如:c=2*max(a,b);3.函數參數函數調用作為一個函數的實參。例如:m=max(a,max(b,c));其中max(b,c)是一次函數調用,它的值作為max另一次調用的實參。m的值是a、b、c三者中的最大者。又如:printf("%d",max(a,b));也是把max(a,b)作為printf函數的一個參數。函數調用作為函數的參數,實質上也是函數表達式形式調用的一種,因為函數的參數本來就要求是表達式形式?!?.4.3對被調用函數的聲明和函數原型在一個函數中調用另一函數(即被調用函數)需要具備哪些條件呢?(1)首先被調用的函數必須是已經存在的函數(是庫函數或用戶自己定義的函數)。但光有這一條件還不夠。§8.4.3對被調用函數的聲明和函數原型(3)如果使用用戶自己定義的函數,而該函數的位置在調用它的函數(即主調函數)的后面(在同一個文件中),應該在主調函數中對被調用的函數作聲明。(2)如果使用庫函數,還應該在本文件開頭用#include命令將調用有關庫函數時所需用到的信息“包含”到本文件中來。函數原型的一般形式為(1)函數類型函數名(參數類型1,參數類型2……);(2)函數類型函數名(參數類型1,參數名1,參數類型2,參數名2……);
“聲明”一詞的原文是declaration,過去在許多書中把它譯為“說明”。聲明的作用是把函數名、函數參數的個數和參數類型等信息通知編譯系統,以便在遇到函數調用時,編譯系統能正確識別函數并檢查調用是否合法。(例如函數名是否正確,實參與形參的類型和個數是否一致)。注意:函數的“定義”和“聲明”不是一回事。函數的定義是指對函數功能的確立,包括指定函數名,函數值類型、形參及其類型、函數體等,它是一個完整的、獨立的函數單位。而函數的聲明的作用則是把函數的名字、函數類型以及形參的類型、個數和順序通知編譯系統,以便在調用該函數時系統按此進行對照檢查。#include<stdio.h>voidmain(){floatadd(floatx,floaty);
/*對被調用函數add的聲明*/floata,b,c;scanf("%f,%f",&a,&b);c=add(a,b);printf("sumis%f\n",c);}floatadd(floatx,floaty)/*函數首部*/{floatz;/*函數體*/z=x+y;return(z);}
例8.5對被調用的函數作聲明如果被調用函數的定義出現在主調函數之前,可以不必加以聲明。因為編譯系統已經先知道了已定義函數的有關情況,會根據函數首部提供的信息對函數的調用作正確性檢查。
如果被調用函數的定義出現在主調函數之前,可以不必加以聲明。因為編譯系統已經先知道了已定義函數的有關情況,會根據函數首部提供的信息對函數的調用作正確性檢查。
改寫例8.5#include<stdio.h>floatadd(floatx,floaty)/*函數首部*/{floatz;/*函數體*/z=x+y;return(z);}voidmain(){floata,b,c;scanf("%f,%f",&a,&b);c=add(a,b);printf("sumis%f\n",c);}§8.5函數的嵌套調用嵌套定義就是在定義一個函數時,其函數體內又包含另一個函數的完整定義。C語言不能嵌套定義函數,但可以嵌套調用函數,也就是說,在調用一個函數的過程中,又調用另一個函數。
例8.求三個數中最大數和最小數的差值#include<stdio.h>
intdif(intx,inty,intz);intmax(intx,inty,intz);intmin(intx,inty,intz);voidmain(){inta,b,c,d;scanf("%d%d%d",&a,&b,&c);
d=dif(a,b,c);printf("Max-Min=%d\n",d);}intdif(intx,inty,intz){returnmax(x,y,z)-min(x,y,z);}intmax(intx,inty,intz){intr;r=x>y?x:y;return(r>z?r:z);}intmin(intx,inty,intz){intr;r=x<y?x:y;return(r<z?r:z);}main()調用函數dif輸出結束dif函數max函數調用函數max調用函數minmin函數§8.6函數的遞歸調用
在調用一個函數的過程中又出現直接或間接地調用該函數本身,稱為函數的遞歸調用。C語言的特點之一就在于允許函數的遞歸調用。例如:intf(intx){inty,z;z=f(y);return(2*z);}遞歸調用定義:函數直接或間接的調用自身叫函數的遞歸調用f()調f調f2調f1f1()f2()intf(intx){inty,z;……
z=f(y);…….return(2*z);}intf1(intx){inty,z;……
z=f2(y);…….return(2*z);}intf2(intt){inta,c;……
c=f1(a);…….return(3+c);}這兩種遞歸調用都是無終止的自身調用,只有在某一條件成立時才繼續遞歸調用;否則就不再繼續。例8.7有5個人坐在一起,問第5個人多少歲?他說比第4個人大2歲,問第4個人歲數,他說比第3個人大2歲,再問第3個人歲數,又說比第2個人大2歲,再問第2個人歲數,說比第1個人大2歲,最后問第1個人,他說是10歲,請問第5個人多大?
age(5)=age(4)+2age(4)=age(3)+210n=1age(3)=age(2)+2age(n)=age(2)=age(1)+2age(n-1)+2n>1age(1)=10用數學公式表述可用函數表示上述關系。age(5)age(4)+2age(4)age(3)+2age(2)+2age(3)age(1)+2age(2)=10age(1)=12age(2)=14age(3)age(2)=16=18age(5)age(4)回推遞推一個遞歸問題分為“回推”和“遞推”兩個階段/*用主函數調用age函數,求第5個人的年齡*/#include<stdio.h>voidmain(){printf("%d",age(5));}用函數來描述上述遞歸過程/*求年齡的遞歸函數*/intage(intn){ intc; if(n==1)c=10; elsec=age(n-1)+2; return(c);}age(5)輸出age(5)c=age(4)+2c=age(3)+2c=age(3)+2c=age(3)+2C=10Mainage(5)age(4)age(3)age(2)age(1)age(5)=18age(4)=16age(3)=14age(2)=12age(1)=10例求n的階乘#include<stdio.h>intfac(intn){intf;if(n<0)printf("n<0,dataerror!");elseif(n==0||n==1)f=1;elsef=n*fac(n-1);return(f);}main(){intn,y;printf("Inputaintegernumber:");scanf("%d",&n);
y=fac(n);printf("%d!=%15d",n,y);}§8.7數組作為函數參數§8.7.1數組元素作函數實參由于實參可以是表達式,而數組元素可以是表達式的組成部分,因此數組元素當然可以作為函數的實參,與用變量作實參一樣,是單向傳遞,即“值傳送”方式。
例8.10有兩個數組a和b,各有10個元素,將它們對應地逐個相比(即a[0]與b[0]比,a[1]與b[1]比……)。如果a數組中的元素大于b數組中的相應元素的數目多于b數組中元素大于a數組中相應元素的數目(例如,a[i]>b[i]6次,b[i]>a[i]3次,其中i每次為不同的值),則認為a數組大于b數組,并分別統計出兩個數組相應元素大于、等于、小于的次數。
#include<stdio.h>voidmain(){
intlarge(intx,inty);/*函數聲明*/
inta[10],b[10],i,n=0,m=0,k=0; printf("enterarraya:\n"); for(i=0;i<10;i++) scanf("%d",&a[i]); printf("\n"); printf("enterarrayb:\n"); for(i=0;i<10;i++) scanf("%d",&b[i]); printf("\n");
for(i=0;i<10;i++){ if(large(a[i],b[i])==1)n=n+1; elseif(large(a[i],b[i])==0)m=m+1; elsek=k+1;}printf("a[i]>b[i]%dtimes\na[i]=b[i]%dtimes\na[i]<b[i]%dtimes\n",n,m,k);if(n>k)printf("arrayaislargerthanarrayb\n");elseif(n<k)printf("arrayaissmallerthanarrayb\n");elseprintf("arrayaisequaltoarrayb\n");}large(intx,inty){ intflag; if(x>y)flag=1; elseif(x<y)flag=-1; elseflag=0; return(flag);}運行情況如下:enterarraya:
1357986420↙
enterarrayb:
5389–1–35604↙
a[i]>b[i]4timesa[i]=b[i]1
timesa[i]<b[i]5
timesarrayaissmallerthannarrayb§8.7.2數組名作函數參數
可以用數組名作函數參數,此時形參應當用數組名或用指針變量。例8.11有一個一維數組score,內放10個學生成績,求平均成績。#include<stdio.h>voidmain(){float
average(floatarray[10]);/*函數聲明*/floatscore[10],aver;inti;printf(″input10scores:\n″);for(i=0;i<10;i++)scanf(″%f″,&score[i]);printf(″\n″);aver=average(score
);printf
(″averagescoreis%5.2f\n″,aver);}floataverage
(floatarray[10]){inti;floataver,sum=array[0];for(i=1;i<10;i++)sum=sum+array[i];aver=sum/10;return(aver);}運行情況如下:input10scores:
100567898.576879967.57597↙
averagescoreis83.40
例8.12形參數組不定義長度#include<stdio.h>voidmain(){floataverage(floatarray[],intn)
floatscore_1[5]={98.5,97,91.5,60,55};floatscore_2[10]={67.5,89.5,99,69.5,77,89.5,76.5,54,60,99.5};printf(“theaverageofclassAis%6.2f\n”,average(score_1,5));printf(“theaverageofclassBis%6.2f\n”,average(score_2,10));}floataverage(floatarray[],intn){inti;floataver,sum=array[0];for(i=1;i<n;i++)sum=sum+array[i];aver=sum/n;return(aver);}
運行結果如下:theaverageofclassAis80.40TheaverageofclassBis78.20例8.13用選擇法對數組中10個整數按由小到大排序。所謂選擇法就是先將10個數中最小的數與a[0]對換;再將a[1]到a[9]中最小的數與a[1]對換……每比較一輪,找出一個未經排序的數中最小的一個。共比較9輪。a[0]a[1]a[2]a[3]a[4]3619416394
136941349613469未排序時的情況
將5個數中最小的數1與a[0]對換將余下的4個數中最小的數3與a[1]對換將余下的3個數中最小的數4與a[2]對換將余下的2個數中最小的數6與a[3]對換,至此完成排序程序實例#include<stdio.h>voidmain(){voidsort(intarray[],intn);inta[10],i;printf(“enterthearray\n”);for(i=0;i<10;i++)scanf(”%d”,&a[i]);
sort(a,10);printf(“thesortedarray:\n”);for(i=0;i<10;i++)printf(“%d”,a[i]);printf(“\n”);}
voidsort(intarray[],intn){inti,j,k,t;for(i=0;i<n-1;i++){k=i;for(j=i+1;j<n;j++) if(array[j]<array[k]) k=j;t=array[k];array[k]=array[i];array[i]=t;}}§8.7.3.多維數組名作函數參數程序如下:#include<stdio.h>voidmain(){max_value(intarray[][4]);inta[3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}};printf(″maxvalueis%d\n″,
max_value(a));}
max_value
(intarray[][4]){inti,j,k,max;max=array
[0][0];for(i=0;i<3;i++)for(j=0;j<4;j++)if(array[i][j]>max)max=array
[i][j];return(max);}
運行結果如下:Maxvalueis34§8.8局部變量和全局變量§8.8.1局部變量在一個函數內部定義的變量是內部變量,它只在本函數范圍內有效,也就是說只有在本函數內才能使用它們,在此函數以外是不能使用這些變量的。這稱為“局部變量”。floatf1(inta)/*函數f1*/{intb,c;…a、b、c有效}charf2(intx,inty)/*函數f2*/{inti,j;x、y、i、j有效…}voidmain()/*主函數*/{intm,n;…m、n有效}
(1)主函數中定義的變量(m,n)也只在主函數中有效,而不因為在主函數中定義而在整個文件或程序中有效。主函數也不能使用其他函數中定義的變量。(2)不同函數中可以使用相同名字的變量,它們代表不同的對象,互不干擾。例如,上面在f1函數中定義了變量b和c,倘若在f2函數中也定義變量b和c,它們在內存中占不同的單元,互不混淆。(3)形式參數也是局部變量。例如上面f1函數中的形參a,也只在f1函數中有效。其他函數可以調用f1函數,但不能引用f1函數的形參a。(4)在一個函數內部,可以在復合語句中定義變量,這些變量只在本復合語句中有效,這種復合語句也稱為“分程序”或“程序塊”。說明voidmain(){inta,b;…{intc;c=a+b;c在此范圍內有效
a,b在此范圍內有效…}…}
//可在復合語句中,定義變量§8.8.2全局變量在函數內定義的變量是局部變量,而在函數之外定義的變量稱為外部變量,外部變量是全局變量(也稱全程變量)。全局變量可以為本文件中其他函數所共用。它的有效范圍為從定義變量的位置開始到本源文件結束。
intp=1,q=5;
/*外部變量*/floatf1(inta)/*定義函數f1*/{intb,c;…}charc1,c2;
/*外部變量*/p,q作用域charf2(intx,inty)/*定義函數f2*/{inti,j;…c1,c2作用域}voidmain()/*主函數*/{intm,n;…}
例8.15有一個一維數組,內放10個學生成績,寫一個函數,求出平均分、最高分和最低分。
#include<stdio.h>floatMax=0,Min=0;/*全局變量*/voidmain(){floataverage(floatarray[],intn);floatave,score[10];inti;for(i=0;i<1010;i++)scanf(″%f″,&score[i]);ave=
average(score,10);printf(“max=%6.2f\nmin=%6.2f\naverage=%6.2f\n“,Max,Min,ave);}floataverage(floatarray[],intn)/*定義函數,形參為數組*/{
inti;floataver,sum=array[0];Max=Min=array[0];for(i=1;i<n;i++){if(array[i]>Max)Max=array[i];elseif(array[i]
<Min)Min=array[i];sum=sum+array[i];}aver=sum/n;return(aver);}
運行情況如下:9945789710067.589926643↙max=100.00min=43.00average=77.65
建議不在必要時不要使用全局變量,原因如下:①全局變量在程序的全部執行過程中都占用存儲單元,而不是僅在需要時才開辟單元。②使用全局變量過多,會降低程序的清晰性,人們往往難以清楚地判斷出每個瞬時各個外部變量的值。在各個函數執行時都可能改變外部變量的值,程序容易出錯。因此,要限制使用全局變量。③它使函數的通用性降低了,因為函數在執行時要依賴于其所在的外部變量。如果將一個函數移到另一個文件中,還要將有關的外部變量及其值一起移過去。但若該外部變量與其他文件的變量同名時,就會出現問題,降低了程序的可靠性和通用性。一般要求把C程序中的函數做成一個封閉體,除了可以通過“實參——形參”的渠道與外界發生聯系外,沒有其他渠道。在同一個源文件中,外部變量和局部變量同名,則在局部變量的作用范圍內,外部變量被“屏蔽”,不起作用。例8.16外部變量與局部變量同名#include<stdio.h>inta=3,b=5;/*a,b為外部變量*/a,b作用范圍voidmain()
{inta=8;/*a為局部變量*/局部變量a作用范圍printf(″%d″,max(a,b));全局變量b的作用范圍}max(inta,intb)/*a,b為局部變量*/
{intc;c=a>b?a∶b;形參a、b作用范圍return(c);
}運行結果為8
§8.9變量的存儲類別
§8.9.1動態存儲方式與靜態存儲方式前面已介紹了從變量的作用域(即從空間)角度來分,可以分為全局變量和局部變量。那么從變量值存在的時間(即生存期)角度來分,又可以分為靜態存儲方式和動態存儲方式。所謂靜態存儲方式是指在程序運行期間由系統分配固定的存儲空間的方式。而動態存儲方式則是在程序運行期間根據需要進行動態的分配存儲空間的方式。這個存儲空間可以分為三部分,1.程序區2.靜態存儲區3.動態存儲區程序區靜態存儲區動態存儲區在C語言中每一個變量和函數有兩個屬性:數據類型和數據的存儲類別。對數據類型,讀者已熟悉(如整型、字符型等)。存儲類別指的是數據在內存中存儲的方式。存儲方式分為兩大類:靜態存儲類和動態存儲類。具體包含四種:自動的(auto),靜態的(static),寄存器的(register),外部的(extern)。根據變量的存儲類別,可以知道變量的作用域和生存期。
§8.9.2auto變量函數中的局部變量,如不專門聲明為static存儲類別,都是動態地分配存儲空間的,數據存儲在動態存儲區中。函數中的形參和在函數中定義的變量(包括在復合語句中定義的變量),都屬此類,在調用該函數時系統會給它們分配存儲空間,在函數調用結束時就自動釋放這些存儲空間。因此這類局部變量稱為自動變量。自動變量用關鍵字auto作存儲類別的聲明。例如:intf(inta)/*定義f函數,a為形參*/{autointb,c=3;/*定義b、c為自動變量*/…}8.9.3用static聲明局部變量有時希望函數中的局部變量的值在函數調用結束后不消失而保留原值,即其占用的存儲單元不釋放,在下一次該函數調用時,該變量已有值,就是上一次函數調用結束時的值。這時就應該指定該局部變量為“靜態局部變量”,用關鍵字static進行聲明。通過下面簡單的例子可以了解它的特點。例8.17考察靜態局部變量的值。#include<stdio.h>voidmain(){intf(int);inta=2,i;for(i=0;i<3;i++)printf(″%d″,f(a));}intf(inta){autointb=0;staticc=3;b=b+1;c=c+1;return(a+b+c);}對靜態局部變量的說明:(1)靜態局部變量屬于靜態存儲類別,在靜態存儲區內分配存儲單元。在程序整個運行期間都不釋放。而自動變量(即動態局部變量)屬于動態存儲類別,占動態存儲區空間而不占靜態存儲區空間,函數調用結束后即釋放。(2)對靜態局部變量是在編譯時賦初值的,即只賦初值一次,在程序運行時它已有初值。以后每次調用函數時不再重新賦初值而只是保留上次函數調用結束時的值。而對自動變量賦初值,不是在編譯時進行的,而是在函數調用時進行,每調用一次函數重新給一次初值,相當于執行一次賦值語句。(3)如在定義局部變量時不賦初值的話,則對靜態局部變量來說,編譯時自動賦初值0(對數值型變量)或空字符(對字符變量)。而對自動變量來說,如果不賦初值則它的值是一個不確定的值。這是由于每次函數調用結束后存儲單元已釋放,下次調用時又重新另分配存儲單元,而所分配的單元中的值是不確定的。(4)雖然靜態局部變量在函數調用結束后仍然存在,但其他函數是不能引用它的。例8.18輸出1到5的階乘值。#include<stdio.h>voidmain(){intfac(intn);inti;for(i=1;i<=5;i++)printf(″%d!=%d\n″,i,fac(i));}Intfac(intn){staticintf=1;f=f*n;return(f);}8.9.4register變量
一般情況下,變量(包括靜態存儲方式和動態存儲方式)的值是存放在內存中的。當程序中用到哪一個變量的值時,由控制器發出指令將內存中該變量的值送到運算器中。經過運算器進行運算,如果需要存數,再從運算器將數據送到內存存放。如果有一些變量使用頻繁(例如在一個函數中執行10000次循環,每次循環中都要引用某局部變量),則為存取變量的值要花費不少時間。為提高執行效率,C語言允許將局部變量的值放在CPU中的寄存器中,需要用時直接從寄存器取出參加運算,不必再到內存中去存取。由于對寄存器的存取速度遠高于對內存的存取速度,因此這樣做可以提高執行效率。這種變量叫做寄存器變量,用關鍵字register作聲明。例如,例8.19中的程序是輸出1到n的階乘的值。例8.19使用寄存器變量#include<stdio.h>voidmain(){longfac(long);longi,n;scanf("%ld",&n);for(i=1;i<=n;i++)printf("%ld!=%ld\n",i,fac(i));}longfac(longn){registerlongi,f=1;/*定義寄存器變量*/for(i=1;i<=n;i++)f=f*i;return(f);}8.9.5用extern聲明外部變量外部變量是在函數的外部定義的全局變量,它的作用域是從變量的定義處開始,到本程序文件的末尾。在此作用域內,全局變量可以為程序中各個函數所引用。編譯時將外部變量分配在靜態存儲區。有時需要用extern來聲明外部變量,以擴展外部變量的作用城。1.在一個文件內聲明外部變量例8.20用extern聲明外部變量,擴展它在程序文件中的作用域。#include<stdio.h>voidmain(){intmax(int,int);/*外部變量聲明*/externA,B;printf("%d\n",max(A,B));}intA=13,B=-8;/*定義外部變量*/intmax(intx,inty)/*定義max函數*/{intz;z=x>y?x:y;return(z);}2.在多文件的程序中聲明外部變量例8.21用extern將外部變量的作用域擴展到其他文件。本程序的作用是給定b的值,輸入a和m,求a×b和am的值。文件file1.c中的內容為:#include<stdio.h>intA;/*定義外部變量*/voidmain(){intpower(int);/*函數聲明*/intb=3,c,d,m;printf(″enterthenumberaanditspowerm:\n″);scanf(″%d,%d″,&A,&m);c=A*b;printf(″%d*%d=%d\n″,A,b,c);d=power(m);printf(″%d**%d=%d\n″,A,m,d);
}文件file2.c中的內容為:externA;/*聲明A為一個已定義的外部變量*/intpower(intn);{inti,y=1;for(i=1;i<=n;i++)y*=A;return(y);}8.9.6用static聲明外部變量有時在程序設計中希望某些外部變量只限于被本文件引用,而不能被其他文件引用。這時可以在定義外部變量時加一個static聲明。例如:file1.cfile2.cstaticintA;externintA;voidmain()voidfun(intn){{……A=A*n;}8.9.7關于變量的聲明和定義對變量而言,聲明與定義的關系稍微復雜一些。在聲明部分出現的變量有兩種情況:一種是需要建立存儲空間的(如:inta;),另一種是不需要建立存儲空間的(如:externa;)。前者稱為“定義性聲明”(definingdeclaration),或簡稱定義(definition)。后者稱為“引用性聲明”(referencingdeclaration)。廣義地說,聲明包括定義,但并非所有的聲明都是定義。對“inta;”而言,它既是聲明,又是定義。而對“externa;”而言,它是聲明而不是定義。一般為了敘述方便,把建立存儲空間的聲明稱定義,而把不需要建立存儲空間的聲明稱為聲明。顯然這里指的聲明是狹義的,即非定義性聲明。例如:voidmain(){externA;/*是聲明不是定義。聲明A是一個已定義的外部變量*/…}intA;§8.9.8存儲類別小結下面從不同角度做些歸納:(1)從作用域角度分,有局部變量和全局變量。它們采用的存儲類別如下:局部變量|自動變量,即動態局部變量(離開函數,值就消失)
|靜態局部變量(離開函數,值仍保留)
|寄存器變量(離開函數,值就消失)
|(形式參數可以定義為自動變量或寄存器變量)全局變量|靜態外部變量(只限本文件引用)|外部變量(即非靜態的外部變量,允許其他文件引用)(2)從變量存在的時間(生
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 202520加盟連鎖店合同協議樣本
- 2024年九月幼兒園駕駛員文明禮儀服務標準聘用協議
- 2025二手設備租賃合同范本
- oem啤酒合同樣本
- 二零二五工地門衛勞動合同范文
- 服裝店鋪轉讓協議范例二零二五年
- 電梯臨時使用協議書范文
- 付款保密協議合同樣本
- 預售商品房抵押合同二零二五年
- 二零二五版公租房承租資格確認
- 數字孿生機場數據融合-全面剖析
- (二模)咸陽市2025年高考模擬檢測(二)語文試卷(含答案)
- 第四單元 第2課時 三位數加三位數2(連續進位加法)(教學設計)-2024-2025學年三年級上冊數學人教版
- 融資部業績考核及獎勵辦法
- 2025-2030中國探地雷達行業發展分析及發展趨勢預測與投資價值研究報告
- 《中央八項規定精神學習教育》專項講座
- 2025年交管12123學法減分考試題庫及答案
- 環境政策協同效應-第1篇-深度研究
- 2024年福建省能源石化集團有限責任公司秋季校園招聘153人筆試參考題庫附帶答案詳解
- 棚戶區改造項目(EPC)方案投標文件(技術方案)
- 2025年中國軍用方艙行業市場集中度、企業競爭格局分析報告-智研咨詢發布
評論
0/150
提交評論