《C語言程序設計》第5章 函數寫字字帖_第1頁
《C語言程序設計》第5章 函數寫字字帖_第2頁
《C語言程序設計》第5章 函數寫字字帖_第3頁
《C語言程序設計》第5章 函數寫字字帖_第4頁
《C語言程序設計》第5章 函數寫字字帖_第5頁
已閱讀5頁,還剩106頁未讀 繼續免費閱讀

下載本文檔

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

文檔簡介

第5章函數學習目標本章主要介紹庫函數的使用、函數的定義和調用方法,然后介紹變量的作用域和存儲方式,最后講解內部函數和外部函數的概念。通過本章的學習,需要掌握函數的聲明、定義、調用和參數傳遞,理解函數的遞歸調用過程,理解變量的作用域和生存期的概念,了解內部函數和外部函數的概念。第5章函數程序設計中也是如此,當程序的功能比較多,如果把所有的程序代碼都寫在一個主函數(main函數)中,就會使主函數變得繁雜、冗長,使閱讀和維護程序變得十分困難,因此我們往往會“生產”一些“部件”來分擔主函數的工作,即事先編好一批常用的函數來實現各種不同的功能,這就是模塊化的程序設計思路。圖5.1是一個程序中函數調用的示意圖。第5章函數圖5.1函數調用示意圖第5章函數例如sqrt函數用來求一個數的平方根,strlen函數用來求一個字符串中字符的個數,把它們保存在函數庫中,這些稱為庫函數。再比如有的程序中要反復實現某一功能(例如打印歡迎信息、操作菜單等),就需要編寫能實現這些功能的用戶自定義函數,以減少重復編寫程序段的工作量。圖5.2是從用戶角度對函數的分類。第5章函數圖5.2從用戶角度對函數的分類例5.1輸入兩個整數,計算它們的和并輸出運算結果。#include<stdio.h>voidmain(){

int

add(int

x,inty);//聲明add函數

int

a,b,sum;

printf("請輸入兩個整數:\n");

scanf("%d,%d",&a,&b);sum=add(a,b);//調用add函數printf("兩數之和:sum=%d\n",sum);}int

add(int

x,inty)//定義add函數,完成求和運算{

ints;s=x+y;returns;}運行結果:請輸入兩個整數:8,7兩數之和:sum=15例5.2以下關于結構化程序設計的敘述中正確的是(2010年9月全國計算機等級考試二級C試題選擇題第11題)A)一個結構化程序必須同時由順序,分支,循環三種結構組成B)結構化程序使用goto語句會很便捷C)在C語言中,程序的模塊化是利用函數實現的D)由三種基本結構構成的程序只能解決小規模的問題分析:程序通過多個函數來實現各種不同的功能,這就是模塊化的程序設計思路。正確的是C。例5.3以下選項中關于程序模塊化得敘述錯誤的是(2011年9月全國計算機等級考試二級C試題選擇題第12題)A)把程序分成若干相對獨立的模塊可便于編碼和調試B)把程序分成若干相對獨立,功能單一的模塊,可便于重復使用這些模塊C)可采用自底向上、逐步細化的設計方法把若干獨立模塊組裝成所要求的程序D)可采用自頂向下、逐步細化的設計方法把若干獨立模塊組裝成所要求的程序分析:程序模塊化的設計方法是自頂而下、逐步細化。所以上述表述中錯誤的是C。第5章函數5.1庫函數5.2函數定義和調用5.3 函數的嵌套調用和遞歸調用5.4局部變量和全局變量5.5 變量的存儲方式和生存期5.6內部函數和外部函數5.1庫函數5.1.1標準庫函數5.1.2include命令行5.1.1標準庫函數它是由系統提供的,用戶不必自己定義而直接使用它們,只需在程序前包含有該函數定義的頭文件。5.1.1標準庫函數必須掌握或熟悉的庫函數有:(1)數學函數(頭文件math.h):abs()、fabs()、sin()、cos()、tan()、asin()、acos()、atan()、exp()、sqrt()、pow()、fmod()、log()、log10()。(2)字符串處理函數(頭文件string.h):strcmp()、strcpy()、strcat()、strlen()。(3)字符處理函數(頭文件ctype.h):isalpha()、isdigit()、islower()、isupper()、isspace()。5.1.1標準庫函數(4)輸入輸出函數(頭文件stdio.h):getchar()、putchar()、gets()、puts()、fopen()、fclose()、fprintf()、fscanf()、fgetc()、fputc()、fgets()、fputs()、feof()、rewind()、fread()、fwrite()、fseek()。(5)動態存儲分配函數(頭文件stdlib.h):malloc()、calloc()、realloc()、free()。5.1.2include命令行在程序中調用庫函數時,由于庫函數并不是由用戶在本文件中定義的,而是存放在函數庫中的,因此要將調用庫函數時所需的信息包含在#include指令指定的“頭文件”中。格式:#include<文件名>5.1.2include命令行功能:雙引號中的文件名即庫函數的文件了,一般存在于編譯器目錄下面,在計算機中搜索"*.h",就會找到庫函數的文件了。將庫函數包含到當前的程序內,將指定的文件與當前的源程序組成一個文件。例5.4輸出1到10數字的平方根和立方值。#include<stdio.h>#include<math.h>voidmain(){

intx=1;doublesquareroot,power;

while(x<=10){

squareroot=sqrt(x);//調用sqrt平方根函數

power=pow(x,3);//調用pow函數求x的立方

printf("%d的平方根:%3.2f\t%d的立方:%5.0f\n",

x,squareroot,x,power); x++;}}運行結果:1的平方根:1.001的立方:12的平方根:1.412的立方:83的平方根:1.733的立方:274的平方根:2.004的立方:645的平方根:2.245的立方:1256的平方根:2.456的立方:2167的平方根:2.657的立方:3438的平方根:2.838的立方:512 9的平方根:3.009的立方:72910的平方根:3.1610的立方:1000例5.5用程序實現大小寫字母轉換。#include<stdio.h>#include<ctype.h>voidmain(){ charmsg1,msg2,toupper,tolower;

printf("請輸入一個小寫字母:"); msg1=getchar();

toupper=toupper(msg1);//調用toupper函數把小寫轉換為大寫

printf("轉換為大寫:%c\n",toupper);

printf("請輸入一個大寫字母:"); msg2=getchar();

tolower=tolower(msg2);//調用tolower函數把大寫轉換為小寫

printf("轉換為小寫:%c\n",tolower);}運行結果:請輸入一個小寫字母:r轉換為大寫:R請輸入一個大寫字母:Q轉換為小寫:q5.2函數定義和調用5.2.1函數定義的語法5.2.2函數的返回值5.2.3函數調用形式5.2.4函數說明5.2.5調用函數與被調用函數之間的數據傳遞5.2.1函數定義的語法C語言要求,在程序中使用的函數,必須“先定義,后使用”。程序設計者需要在程序中自己定義想用的而庫函數并沒有提供的函數,指定函數名字、函數返回值類型、函數實現的功能以及參數的個數與類型,將這些信息通知編譯系統。5.2.1函數定義的語法1.定義無參函數無參函數是指函數名后面的括號是空的,沒有任何參數。無參函數可以帶回或不帶回函數值,一般用來執行指定的一組操作。格式:類型名函數名(){ 函數體 }

例如,函數getchar()就是無參函數,它帶回的函數返回值就是鍵盤輸入的字符。5.2.1函數定義的語法2.定義有參函數有參函數。在調用函數時,主調函數在調用被調用函數時,通過參數向被調用函數傳遞數據,一般情況下,執行被調用函數時會得到一個函數值,供主調函數使用。格式:類型名函數名(形式參數表列){ 函數體 }例5.6編寫求兩個整數較大者的函數。intmax(intx,inty){

intz

;z=x>y

?x

:y

;return(z)

;}分析:這是一個函數定義的例子,intmax(intx,inty)是函數的首部。int是類型名,說明函數返回一個int類型的值;max是函數名,是用戶命名的標識符;函數名后面的括號里是形式參數表列(形參),這里有x和y兩個,它們的類型是int,注意形參要逐個定義,各形參之間用逗號分開。5.2.2函數的返回值通常,函數調用的目的是使主調函數獲得一個確定的值,即函數的返回值。例如,我們在前面介紹的調用語句中有c=max(a,b);這就是調用函數max(a,b),函數執行后返回一個函數值,并把值賦給變量c。5.2.2函數的返回值函數的返回值是通過函數中的return語句獲得的。如果需要從被調用函數帶回一個函數值,則被調用函數中必須包含return語句。5.2.2函數的返回值對于函數的返回值有如下說明:(1)一個函數中可以有一個以上的return語句,但被執行的只有一個,即返回值只有一個,執行到哪一個return語句,哪一個return語句就起作用。(2)return語句的形式可以是:return表達式;或return(表達式);或return;例如:return(x>y?x:y);(3)函數返回值的類型,應當是定義函數時指定的函數類型。例如函數定義為:floatadd(float

x,floaty){return(x+y);}函數類型為float,則通過return語句返回的值類型也應為float。如果函數類型和return語句中表達式的值類型不一致,則以函數類型為準。(4)對于不帶回值的函數,定義函數為“void類型”。此時在函數體中不得出現return語句。例5.7以下關于return語句的敘述中正確的是(2010年3月全國計算機等級考試二級C試題選擇題第24題)A)一個自定義函數中必須有一條return語句B)一個自定義函數中可以根據不同情況設置多條return語句C)定義成void類型的函數中可以有帶返回值的return語句D)沒有return語句的自定義函數在執行結束時不能返回到調用處分析:按照return語句的特點,正確的是B。5.2.3函數調用形式定義函數的目的是為了調用此函數,以實現所需的操作。1.函數調用函數調用的一般形式:格式:函數名(實際參數表)例如:m=getchar();//調用無參函數

c=max(a,b);//調用有參函數2.函數調用的方式按函數調用在程序中出現的形式和位置來分,可以用以下幾種方式調用函數:(1)函數表達式函數調用出現在另一個表達式中,用函數的返回值參與表達式的運算。這種方式要求函數是有返回值的。例如:c=max(a,b);這是一個賦值表達式,把函數max(a,b)的返回值賦予變量c。(2)函數調用語句函數調用的一般形式加上分號即構成單獨的一個語句。例如:printf(“%d”,a);scanf(“%d”,&b);(3)函數參數函數調用作為另一個函數的實參。例如:m=max(a,max(b,c));其中max(b,c)是一次函數調用,它的值作為max()另一次調用的實參。經過賦值后,m的值是a,b,c三者最大者。例5.8有以下程序(2009年9月全國計算機等級考試二級C試題選擇題第24題)#include<stdio.h>voidfun(intp){ intd=2; p=d++; printf("%d",p);}main(){ inta=1; fun(a);printf("%d\n",a); }程序運行后的輸出結果是:A)32 B)12 C)21 D)22分析:main()調用fun(),將實參a的值,拷貝一份傳遞給形參p,因此p也等于1;fun()函數體內執行了p=d++,自增運算符在d的后面,先取值后加1,則p=2,d=3,第一個打印結果為2;函數調用結束,形參不影響實參的值,因此實參a保持原值,第2個打印結果為1,選C。例5.9有以下函數(2011年3月全國計算機等級考試二級C試題填空題第9題)voidprt(char

ch,intn){inti;

for(i=1;i<=n;i++)

printf(i%6!=0?"%c":"%c\n",ch);}執行調用語句prt('*',24);后,函數共輸出了

行*號。分析:調用語句prt('*',24);傳遞參數,ch值為'*',n值為24,所以for循環從1到24。而printf語句其實是一個條件語句,首先判斷i%6!=0,如果i的值不能被6整除,則相當于執行printf("%c",ch);如果i的值能被6整除,則相當于執行printf("%c\n",ch);那么,從1~24,只有4次被6整除,所以共換行4次,即函數共輸出了

4行*號。5.2.4函數說明一般情況下,在使用用戶自定義函數,并且被調函數與主調函數在同一個程序文件中時,應在主調函數中對被調函數作函數說明。函數說明的作用是向編譯系統聲明將要調用此函數,并將有關信息通知編譯系統。函數說明的一般形式:格式:類型名函數名(形式參數表列);例如:intadd(intx,inty);或者

int

add(int,int)

;以上兩種形式都可。第二種形式省略了參數名,因為編譯系統不檢查參數名,所以參數名可有可無。例5.10輸入兩個整數,輸出其中較大者。要求用函數來實現。#include<stdio.h>voidmain(){intmax(intx,inty);//在main中對max函數說明

inta,b,c;printf("請輸入兩個整數

:");scanf("%d,%d",&a,&b);c=max(a,b);//調用max函數

printf("較大的數是

:%d\n",c);}intmax(intx,inty)//定義max函數{intz; z=x>y?x:y; returnz;}運行結果:請輸入兩個整數

:7,8較大的數是

:8C語言在以下幾種情況時,可以省略函數說明:(1)當被調函數的函數定義出現在主調函數之前時,在主調函數中可以不對被調函數進行說明而直接調用。(2)如果在所有函數定義之前,即文件開頭,已對各個函數進行了說明(稱為外部聲明),則在后面的主調函數中,可不再對被調函數作說明。(3)對庫函數的調用不需要作說明,只需把該函數的頭文件用include命令包含在源文件前面。5.2.5調用函數與被調用函數之間的數據傳遞函數參數分為形式參數和實際參數,函數定義時出現的是形式參數。當主調函數調用有參函數時,調用語句中出現的是實際參數。形式參數和實際參數的功能是作數據傳遞。函數調用時,主調函數把實際參數的值傳送給被調函數的形式參數,從而實現主調函數向被調函數的數據傳遞。實際參數與形式參數個數應相等,類型應一致。傳遞參數時,實際參數與形式參數按順序對應,一一傳遞數據。例5.11有以下程序(2009年3月全國計算機等級考試二級C試題選擇題第24題)#include<stdio.h>int

f(int

x,inty){return((y-x)*x);}main(){inta=3,b=4,c=5,d;d=f(f(a,b),f(a,c));

printf("%d\n",d);}程序運行后的輸出結果是:A)10 B)9 C)8 D)7分析:main()調用f函數,實參是函數f(a,b)和f(a,c)的返回值,所以首先求f(a,b)和f(a,c)的函數值,f(a,b)又是將a,b作為實參傳遞給函數形參,返回函數值3;同樣,f(a,c)返回函數值6;繼續計算函數f(3,6),獲得函數返回值9,賦值給變量d。選B。例5.12有以下程序(2011年3月全國計算機等級考試二級C試題選擇題第33題)#include<stdio.h>int

fun(int

x,inty){if(x!=y)return((x+y)/2);elsereturn(x);}main(){

inta=4,b=5,c=6; printf("%d\n",fun(2*a,fun(b,c)));}程序運行后的輸出結果是:A)3 B)6 C)8 D)12分析:要輸出fun(2*a,fun(b,c))的值,首先求fun(b,c)的函數值,b、c作為實參傳遞給函數形參,返回函數值5,注意fun函數的類型定義為int,所以返回值取整;繼續計算函數fun(8,5),獲得函數返回值6。選B。例5.13有以下程序(2011年9月全國計算機等級考試二級C試題選擇題第24題)#include<stdio.h>doublef(doublex);main(){doublea=0;inti;for(i=0;i<30;i+=10)a+=f((double)i);printf("%5.0f\n",a);}doublef(doublex){returnx*x+1;}程序運行后的輸出結果是:A)503B)401C)500D)1404分析:本題通過for循環3次調用f函數。第1次調用i=0,函數返回1,則a=1;第2次調用i=10,函數返回101,則a=102;第3次調用i=20,函數返回401,則a=503;循環條件已不成立,所以循環結束。選A。5.3 函數的嵌套調用和遞歸調用5.3.1 函數的嵌套調用5.3.2函數的遞歸調用5.3.1 函數的嵌套調用C語言函數的定義是互相獨立、互相平行的,函數之間不存在從屬關系。一個函數內不能定義另一個函數,即不能嵌套定義,但可以嵌套調用函數,也就是說,在調用一個函數的過程中,又調用另一個函數。函數的嵌套調用過程如圖5.3所示。main()函數f1()函數f2()函數調用f1()函數調用f2()函數結束返回返回①②③④⑤⑥⑦⑧⑨圖5.3函數的嵌套調用圖5.3中所示的是兩層嵌套,其執行過程是:(1)執行main函數的開頭部分。(2)在main中調用f1函數,程序轉到f1函數。(3)執行f1函數過程中,調用f2函數,程序轉到f2函數。(4)在f2函數執行完后,程序返回到f1函數中調用f2函數的位置。(5)繼續執行f1函數中未執行的部分,執行完后返回到main函數調用f1函數的位置。(6)繼續執行main函數直到程序結束。例5.14輸入3個整數,找出其中最大的數。用函數的嵌套調用來處理。#include<stdio.h>voidmain(){

intmax3(inta,int

b,intc);//對max3的函數聲明

int

a,b,c,max;

printf("請輸入3個整數:");

scanf("%d%d%d",&a,&b,&c); max=max3(a,b,c);//調用max3函數,找出3個數中最大數

printf("最大的數是:%d\n",max);}intmax3(inta,int

b,intc)//定義max3函數{

intmax2(inta,intb);//對max2的函數聲明

intm; m=max2(a,b);//調用max2函數,找出a、b中較大者,放在m中

m=max2(m,c);//調用max2函數,找出m、c中較大者,即3個數中最大者

return(m);}intmax2(inta,intb)//定義max2函數{

if(a>=b) returna;//若a較大,則返回a else returnb;//否則返回b}運行結果:請輸入3個整數

:785最大的數是

:8分析:從代碼可以看到,程序由主函數、max3函數、max2函數組成。主函數調用max3函數,max3函數調用max2函數,形成嵌套調用。5.3.2函數的遞歸調用在調用一個函數的過程中又出現直接或間接地調用該函數本身,稱為函數的遞歸調用。C語言允許函數的遞歸調用。在調用函數f的過程中,又要調用f函數,這是直接遞歸調用,見圖5.4。如果在調用f1函數過程中又要調用f2函數,而在調用f2函數過程中又要調用f1函數,就是間接遞歸調用,見圖5.5。5.3.2函數的遞歸調用f函數調用f函數f1函數調用f2函數f2函數調用f1函數

圖5.4直接遞歸調用

圖5.5間接遞歸調用

5.3.2函數的遞歸調用可以看到,無論是直接遞歸調用還是間接遞歸調用,都會形成無終止的自身調用,這在程序中是不允許的。為了結束遞歸調用,可以用if語句來控制,只有在某一條件成立時才繼續執行遞歸調用,否則就不再繼續。5.3.2函數的遞歸調用例5.15從鍵盤輸入一個正整數n,用遞歸方法求n!。分析:由于n!=n*(n-1)!,所以要計算n!,就必須先計算(n-1)!,而要知道(n-1)!,必須先計算(n-2)!,以此類推,要求2!,必須先知道1!,而1!=1。以上關系可用下面的遞歸公式表示:n!=n!=1(n=0,1)n*(n-1)!(n>1)程序代碼如下:#include<stdio.h>voidmain(){

int

fac(intn);//fac函數聲明

intn;

inty;

printf("請輸入一個正整數:");

scanf("%d",&n);//輸入要求階乘的數

y=fac(n);

printf("%d!=%d\n",n,y);}int

fac(intn)//定義fac函數{

intf;

if(n<0)//n不能小于0

printf("n<0,錯誤!"); elseif(n==0||n==1)//n=0或1時,n!=1,結束遞歸

f=1; elsef=fac(n-1)*n;//n>1時,n!=n*(n-1)! return(f);}運行結果:請輸入一個正整數:55!=120例5.16設有如下函數定義(2011年3月全國計算機等級考試二級C試題選擇題第32題)int

fun(intk){if(k<1)return0;elseif(k==1)return1;elsereturnfun(k-1)+1;}若執行調用語句:n=fun(3),則函數fun總共被調用的次數是A)2 B)3 C)4 D)5分析:這是一個函數遞歸調用的例題。當執行fun(3)時,形參k的值為3,執行函數語句,由于不滿足k<1或k==1,則遞歸調用fun(2),同樣的情況,再次遞歸調用fun(1),這時返回函數值1,所以總共調用fun函數3次。選B。例5.17有以下程序(2011年9月全國計算機等級考試二級C試題填空題第11題)#include<stdio.h>voidfun(intx){if(x/5>0)fun(x/5);

printf("%d",x);}main(){fun(11);printf("\n");}程序運行后的輸出結果是

。分析:這也是函數遞歸調用的程序。當執行fun(11)時,因為11/5=2>0,所以遞歸調用fun(2),這時不再滿足if條件,則執行printf語句,輸出2;接著回到上一層調用點,繼續后面語句的執行,輸出11,所以程序運行的結果是2_11。這里2_11之間的短橫表示輸出時的空格。printf()函數是格式輸出函數,輸出時應注意它的格式要求。5.4局部變量和全局變量5.4.1 局部變量5.4.2全局變量在C語言中,一個變量能夠起作用(被引用)的程序范圍,稱為變量的作用域,即一個變量定義之后,在何處能夠使用該變量。若變量在整個程序范圍內的各個函數中均可被訪問,稱其為全局變量。如果變量僅在特定的函數或程序塊內可被訪問,不能被其他函數或程序訪問,稱其為局部變量。5.4.1 局部變量上面提過,變量的作用域決定于變量的定義位置。在一個函數內部定義的變量只在本函數范圍內有效,在此函數以外是不能使用這些變量的;在復合語句內定義的變量只在本復合語句范圍內有效,只有在本復合語句內才能引用它們。這些都是局部變量。

例如:floatf1(inta)//a,b,c在f1函數中有效{int

b,c;...}intf2(intx,inty)//x,y,i,j在f2函數中有效{int

i,j

;{

intc;//c在復合語句中定義,只在此語句范圍內有效

c=i+j;}...}voidmain()//m,n在main函數中有效{int

m,n;...}5.4.2全局變量全局變量也稱為外部變量,它是在函數之外定義的變量。全局變量不屬于某一個特定的函數,它可以為本文件中其他函數所共用,它的有效范圍為從定義變量的位置開始到本源文件結束。5.4.2全局變量int

a,b;//定義外部變量a,b,全程序有效floatf1()//定義f1函數{...}floatx,y;//定義外部變量x,y,從定義到程序結束都有效intf2(){

inti;//i在f2函數中定義,是局部變量

...}voidmain()//定義main函數{...}5.4.2全局變量設置全局變量的作用是增加函數間數據聯系。由于全局變量是各個函數都能引用的變量,因此如果在一個函數中改變了全局變量的值,就能影響到其他函數中全局變量的值,相當于各個函數間有直接的傳遞通道。例5.18輸入正方體的長寬高l,w,h,求其體積即3個面的面積。#include<stdio.h>ints1,s2,s3;//定義全局變量s1,s2,s3int

vs(int

a,int

b,intc)//定義求體積及面積的函數vs{

intv; v=a*b*c; s1=a*b;//函數可訪問全局變量

s2=b*c;//為s1,s2,s3賦值

s3=a*c; returnv;//返回體積值}voidmain(){

int

v,l,w,h;

printf("請輸入正方體的長寬高:");

scanf("%d%d%d",&l,&w,&h); v=vs(l,w,h);//調用vs函數求體積、面積

printf("正方體的體積v=%d,各面面積s1=%d,s2=%d,s3=%d\n",v,s1,s2,s3);}運行結果:請輸入正方體的長寬高:345正方體的體積v=60,各面面積s1=12,s2=20,s3=15分析:本例通過函數來計算正方體的體積及3個面的面積,但函數只能返回一個值,所以把3個面積定義為全局變量,這樣一般函數和主函數都可以訪問,我們在函數中計算出面積值,賦給全局變量,主函數也訪問這些變量來獲取它們的值。這也反映出全局變量是實現函數之間數據通訊的有效方法。例5.19分析下面程序。#include<stdio.h>inta=3,b=5;//定義全局變量a,bvoidmain(){

int

add(int

a,intb);//函數聲明

inta=8;//定義局部變量a

printf("兩數之和:%d\n",add(a,b));//調用add函數,a,b為實參}int

add(int

x,inty){ return(x+y);//求兩數之和}運行結果:兩數之和:13分析:在本例中,既定義了全局變量a、b,也定義了同名局部變量a。局部變量a是在主函數中定義的,其作用域為主函數,在此范圍內,全局變量a就失去作用,所以當調用函數add(a,b)時,傳遞的實參a是局部變量a,而不是全局變量a。所以輸出結果為13。例5.20有以下程序(2009年9月全國計算機等級考試二級C試題填空題第11題)#include<stdio.h>inta=5;voidfun(intb){inta=10;a+=b;printf("%d",a);}main(){intc=20;fun(c);a+=c;printf("%d\n",a);}程序運行后的輸出結果是

。分析:程序首先定義了全局變量a=5。當執行主函數main時,會調用fun函數,fun函數中定義了局部變量a=10,則全局變量a失去作用,所以輸出30。返回到主函數調用點繼續執行,主函數中的a是全局變量a,所以輸出25,因此程序運行的結果是3025。5.5 變量的存儲方式和生存期5.5.1 動態存儲方式與靜態存儲方式5.5.2 局部變量的存儲類別5.5.3 全局變量的存儲類別5.5.4 存儲類別小結5.5.1 動態存儲方式與靜態存儲方式在C語言中,變量不僅有確定的數據類型定義,還有存儲方式的定義。變量的存儲方式是其存儲屬性,即變量在內存中的存儲方法,不同的存儲方法,將影響變量值存在的時間(即生存期)。變量的生存期是指程序在運行期間,從給變量分配存儲單元,到所分配的存儲單元被系統收回的那段時間。有的變量在程序運行的整個過程都是存在的,始終占有固定的存儲單元;而有的變量則是在調用其所在的函數時才臨時分配存儲單元,而在函數調用結束后該存儲單元就馬上釋放了,變量也就不存在了在C語言中,變量的存儲有兩種方式:靜態存儲方式和動態存儲方式。靜態存儲方式是指在程序運行期間由系統分配固定的存儲空間的方式,而動態存儲方式則是在程序運行期間根據需要進行動態的分配存儲空間的方式。內存中提供給程序的存儲空間通常分為3部分,如圖5.6所示。程序區靜態存儲區動態存儲區圖5.6程序占用的內存空間程序的數據分別存放在靜態存儲區和動態存儲區中。全局變量全部存放在靜態存儲區中,在程序開始執行時就給全局變量分配存儲區,程序執行完畢即釋放。在程序執行過程中它們占據固定的存儲單元,而不是動態進行分配和釋放。在動態存儲區中則存放以下數據:(1)函數形式參數。在調用函數時給形參分配存儲空間。(2)函數調用時的現場保護和返回地址等。以上數據在函數調用開始時分配動態存儲空間,函數結束時釋放這些空間。注意在兩次調用同一函數時,每次分配給局部變量的存儲空間不一定相同。在定義和聲明變量時,一般應同時指定其數據類型和存儲類別。如果不指明存儲類別,表示采用默認方式(即系統隱含指定的某一種存儲類別)。所以變量的完整定義形式為:格式:存儲類別數據類型變量名;C的存儲類別包括4種:自動(auto)、靜態(static)、寄存器(register)、外部(extern)。不同的存儲類別,將影響到變量的生存期以及作用域。5.5.2 局部變量的存儲類別1.自動變量(auto)在函數內或復合語句中定義的變量,如果不指定其存儲類別為static,那么就是自動存儲變量,其關鍵字為auto。自動存儲變量都是動態地分配存儲空間的,數據存儲在動態存儲區中,即調用函數時,系統會給這些變量分配存儲空間,在函數調用結束時就自動釋放這些存儲空間。

例如:intfun(){autointnum;//定義num為自動變量

…}實際上,我們在定義這類局部變量時,往往省略其存儲類別,因為關鍵字“auto”可以省略,不寫auto則隱含指定為自動存儲類別。所以以上函數定義可寫作:intfun(){

intnum;//定義num為自動變量

…}2.靜態局部變量(static)有時需要函數中局部變量的值在函數調用結束后不消失,即其占用的存儲單元在整個程序運行期間都不釋放,那么可以將該局部變量定義為靜態局部變量,用關鍵字static進行聲明。例如:staticint

m,n;例5.21靜態局部變量與自動局部變量的比較。#include<stdio.h>voidmain(){ voidtest();//test函數聲明

int

i,a=0;//自動局部變量a,在主函數中定義

for(i=0;i<3;i++) test();//調用test() {

inta=10;//自動局部變量a,在復合語句中定義

a=a*10;

printf("復合語句中:a=%d\n",a); } a=a+10;

printf("主函數中:a=%d\n",a);}voidtest(){ staticinta=5;//靜態局部變量a,賦初值一次

printf("靜態變量:a=%d\n",a); a=a+10;}運行結果:靜態變量:a=5靜態變量:a=15靜態變量:a=25復合語句中:a=100主函數中:a=10分析:首先在主函數中循環調用test()函數,函數中的靜態局部變量a,在每次函數調用結束后,它的值并不消失,所以循環調用時其值能夠保持連續性。復合語句和主函數中定義了同名自動局部變量a,注意其不同的作用域。例5.22若函數中有定義語句:intk;則(2009年3月全國計算機等級考試二級C試題選擇題第12題)A)系統將自動給k賦初值0B)這時k中的值無定義C)系統將自動給k賦初值-1D)這時k中無任何值分析:函數中定義intk;則k應是自動存儲變量,它不同于靜態存儲變量,系統不會自動給k賦值,所以這時k中的值無定義。選B。例5.23有以下程序(2011年3月全國計算機等級考試二級C試題選擇題第34題)#include<stdio.h>intfun(){staticintx=1;x*=2;returnx;}main(){int

i,s=1;for(i=1;i<=3;i++)s*=fun();printf("%d\n",s);}程序運行后的輸出結果是:A)0B)10C)30D)64分析:主函數中通過循環,共3次調用fun()函數。在fun()函數中,定義了靜態局部變量x=1。第一次調用fun(),返回x值為2;第二次調用fun(),由于靜態變量保留上一次函數調用的結果,所以乘以2后返回x值為4;第三次調用fun(),返回x值為8,所以在主函數中,s=2*4*8=64。選D。例5.24有以下程序(2009年9月全國計算機等級考試二級C試題選擇題第34題)#include<stdio.h>

int

f(intn);main(){inta=3,s;

s=f(a);s=s+f(a);printf("%d\n",s);}

int

f(intn){staticinta=1;n+=a++;returnn;}

程序運行以后的輸出結果是:

A)7B)8C)9D)10分析:在函數f()中定義了靜態變量a,第1次調用f函數,返回4,同時在函數中靜態變量a的值自增為2;第2次調用f函數,靜態變量會保留上一次的結果,所以返回5,則s=9。選C。例5.25有以下程序(2011年9月全國計算機等級考試二級C試題選擇題第33題)#include<stdio.h>int

f(intm){staticintn=0; n+=m; returnn;}main(){intn=0;

printf("%d,",f(++n));printf("%d\n",f(n++));}程序運行后的輸出結果是:A)1,2B)1,1 C)2,3 D)3,3分析:主函數中定義n=0,調用函數f(++n),即f(1),在函數f中定義靜態變量n,加1后返回1;主函數再次調用f(n++),++運算符在變量的后面,所以還是f(1),函數中的靜態變量保留上一次的值,所以在原值1的基礎上加1,返回2,最終輸出1,2。選A。3.寄存器變量(register)一般情況下,變量值是存放在內存中的,但我們也知道,計算機的CPU內部包含若干個通用寄存器,硬件在對數據操作時,常常是先把數據取到寄存器(或一部分取到寄存器),然后進行操作。而且,計算機對寄存器中數據的操作速度要遠遠快于對內存中數據的操作速度。為提高程序的運行速度,可將使用十分頻繁的局部變量說明為寄存器變量,將其存儲在CPU的寄存器中。寄存器變量定義的關鍵字是register,例如:registerintn;5.5.3 全局變量的存儲類別全局變量(即外部變量)是在函數的外部定義的,它的作用域從變量定義處開始,到本程序文件的末尾。全局變量都存放在靜態存儲區中,占有固定的存儲單元,生存期為程序的整個運行過程。1.在一個文件內聲明外部變量如果外部變量不在文件的開頭定義,其有效的作用范圍只限于定義處到文件結束。如果想在定義點之前引用該外部變量,解決的方法是在引用之前用關鍵字extern對該變量作“外部變量聲明”。表示該變量是一個已經定義的外部變量。有了此聲明,就可以從“聲明”處起,合法地使用該外部變量。例5.26求兩數中較大者。#include<stdio.h>voidmain(){

int

max(int

x,inty);//函數聲明externintA,B;//全局變量聲明printf("兩數中較大的是:%d\n",max(A,B));//使用全局變量作為實參}intmax(intx,inty)//函數定義{intz;z=x>y?x:y;return(z);}intA=15,B=-8;//定義全局變量A,B運行結果:兩數中較大的是:15分析:在本程序文件的最后定義了外部變量A、B,其作用域是從定義處開始的,那么在主函數main中不能引用外部變量A、B。現在main函數中用extern對A和B進行“外部變量聲明”,所以從“聲明”處起,可以合法地使用該外部變量A和B。用extern聲明外部變量時,類型名可以寫也可以不寫。例如,“externintA,B;”也可以寫成“externA,B;”。2.在多文件的程序內聲明外部變量一個C程序可以由一個或多個源程序文件組成。如果程序由多個源程序文件組成,那么在一個文件中想引用另一個文件中已定義的外部變量,有什么辦法呢?實現的方法是:在其中一個文件中定義外部變量,而在另一文件中用extern對這個變量作“外部變量聲明”。在編譯和連接時,編譯系統會由此知道這個變量是一個已在別處定義的外部變量,并將在另一文件中定義的外部變量的作用域擴展到本文件,在本文件中就可以合法地引用該外部變量。例5.27輸入A和m,求Am的值。文件file1.c:#include<stdio.h>intA;//定義外部變量voidmain(){

int

power(int);//函數聲明

int

b,m;

printf("請輸入一個整數和它的方次:");

scanf("%d%d",&A,&m); b=power(m);

printf("%d**%d=%d\n",A,m,b);}文件file2.c:externA;//在file2中聲明file1定義的外部變量A,使其在本文件有效int

power(intn){

int

i,y=1;

for(i=1;i<=n;i++) y*=A; return(y);}運行結果:請輸入一個整數和它的方次:323**2=93.用static聲明外部變量有時在程序

溫馨提示

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

評論

0/150

提交評論