版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
函數(shù)的定義和調(diào)用函數(shù)的嵌套調(diào)用和遞歸調(diào)用變量的作用域和生存期編譯預(yù)處理多源文件C程序的組織方法第4章函數(shù)與程序結(jié)構(gòu)模塊化程序設(shè)計技術(shù)就是通過開發(fā)和維護(hù)一些小的程序塊(即模塊)的方法構(gòu)建一個大型程序,是人類解決較大的復(fù)雜問題所采用的一種“分而治之”的策略。本章主要討論C語言實現(xiàn)模塊化程序設(shè)計技術(shù)的手段以及在模塊化實現(xiàn)過程中所遇到的一系列問題。第4章函數(shù)與程序結(jié)構(gòu)C程序的一般結(jié)構(gòu)C程序源文件1…源文件i源文件n函數(shù)1預(yù)處理語句函數(shù)m說明/定義部分執(zhí)行語句部分圖4.1C程序的一般結(jié)構(gòu)……第4章函數(shù)與程序結(jié)構(gòu)在C語言程序中的若干個函數(shù)中必須有一個且只能有一個函數(shù)成為主函數(shù)。C程序的執(zhí)行從main函數(shù)開始,調(diào)用其它函數(shù)后流程回到main函數(shù),在main函數(shù)中結(jié)束整個程序的運行。在一個函數(shù)中可以使用另一個函數(shù)的功能,這稱為函數(shù)調(diào)用。4.1函數(shù)的定義和調(diào)用
使用函數(shù)首先要對函數(shù)定義。函數(shù)定義中必須描述出函數(shù)的三個特征,即函數(shù)的名字、函數(shù)形式的參數(shù)表以及函數(shù)的返回值類型。C語言函數(shù)定義的形式如下:
4.1.1函數(shù)的定義和聲明返回值類型函數(shù)名(形式參數(shù)列表){函數(shù)體return;}
下面以定義實現(xiàn)求階乘功能函數(shù)為例了解一個函數(shù)的具體定義過程.根據(jù)前面所學(xué)知識知道,求階乘的C程序如下所示:
#include<stdio.h>voidmain(){ int i,n; longfact=1; printf("Inputn:"); scanf("%d",&n); for(i=1;i<=n;i++) fact*=i;printf("%d!=%ld\n",n,fact);}程序?qū)崿F(xiàn)了計算從鍵盤輸入一個整數(shù)n,并求其階乘的功能。如果在今后的應(yīng)用中,需要將求某數(shù)階乘的功能作為程序中相對獨立的一個部分(功能),則需要將上述功能用自編函數(shù)的方式實現(xiàn)
4.1.1函數(shù)的定義和聲明(1)函數(shù)的命名函數(shù)的名字在程序設(shè)計中有兩個作用:一是使用該名字調(diào)用這個函數(shù);二是應(yīng)該見名知意,符合c語言的規(guī)則。對于實現(xiàn)本功能的函數(shù),命名factorial。
(2)函數(shù)執(zhí)行結(jié)果的返回和返回值類型的確定
函數(shù)執(zhí)行的結(jié)果如果是一個具體的表達(dá)式,當(dāng)函數(shù)執(zhí)行完成時用關(guān)鍵字return組成形如:return<表達(dá)式>;的C語句將函數(shù)執(zhí)行的結(jié)果返回給調(diào)用函數(shù)者。注意函數(shù)執(zhí)行結(jié)果的數(shù)據(jù)類型不是由返回的表達(dá)式數(shù)據(jù)類型來決定的,而是用類型名作為關(guān)鍵字在函數(shù)的頭部予以確定?;谏鲜鰞牲c,可以寫出實現(xiàn)階乘功能的函數(shù)factorial4.1.1函數(shù)的定義和聲明long為函數(shù)返回值類型Return語句將n!返回給調(diào)用函數(shù)花括號給函數(shù)確定了邊界區(qū)域longfactorial(){ int i,n; longfact=1;
printf("Inputn:"); scanf("%d",&n); for(i=1;i<=n;i++) fact*=i; returnfact;}4.1.1函數(shù)的定義和聲明(3)函數(shù)的參數(shù)表設(shè)計在上面定義的函數(shù)factorial中,函數(shù)用到的數(shù)據(jù)是從鍵盤輸入獲取的,如果需要從對函數(shù)的調(diào)用者(使用者)處獲取所需要的數(shù)據(jù),就必須對函數(shù)的形式參數(shù)表進(jìn)行設(shè)計。此時需要兩個步驟來實現(xiàn):一是將函數(shù)內(nèi)部用于從鍵盤上接收數(shù)據(jù)的數(shù)據(jù)對象定義移到函數(shù)的形式參數(shù)表中;二是刪去函數(shù)中從鍵盤獲取數(shù)據(jù)的語句。函數(shù)factorial可以改造為如下形式:longfactorial(intn){ int i; longfact=1; for(i=1;i<=n;i++) fact*=i; returnfact;}n的值函數(shù)調(diào)用者(使用者)處獲取,不再從程序中獲取。4.1.1函數(shù)的定義和聲明
通過對函數(shù)factorial定義過程的討論,可以理解C函數(shù)定義一般形式中各個函數(shù)組成成分的確切含義:(1)返回值類型說明符 用以制定函數(shù)返回值的數(shù)據(jù)類型,可以是C語言中任何合法的基本數(shù)據(jù)類型和構(gòu)造體數(shù)據(jù)類型。如果要表示一個函數(shù)不需要向調(diào)用者返回值,則函數(shù)的返回值數(shù)據(jù)類型應(yīng)該定義為void,如前面章節(jié)的主函數(shù)main。(2)函數(shù)的名字 遵循C語言標(biāo)識符的命名規(guī)則。盡量做到“見名知意”。(3)形式參數(shù)表 函數(shù)的形式參數(shù)表用圓括號括起來的、由零個到多個形式參數(shù)的定義組成,兩個形式參數(shù)定義之間用逗號分隔。若一個函數(shù)沒有形式參數(shù),作為函數(shù)運算符使用的圓括號也不能省略。4.1.1函數(shù)的定義和聲明(4)return<表達(dá)式>;語句如果函數(shù)定義中指定的返回值數(shù)據(jù)類型不是void,則函數(shù)定義中必須有用關(guān)鍵字return構(gòu)成的return<表達(dá)式>;語句。當(dāng)函數(shù)執(zhí)行到該C語句時,先計算該語句中的表達(dá)式的值,然后再將該值強(qiáng)制轉(zhuǎn)化為指定的函數(shù)返回值的數(shù)據(jù)類型,返回到主調(diào)函數(shù)中。如果函數(shù)定義時指定的返回值類型是void,則函數(shù)定義中可以沒有用return構(gòu)成的語句,若函數(shù)定義的執(zhí)行流程需要使用return語句,則其形式只能是:return;。4.1.1函數(shù)的定義和聲明例如:我們定義一個求兩個數(shù)中最大值的函數(shù):4.1.1函數(shù)的定義和聲明intmax(intx,inty){inttemp;/*函數(shù)體的局部變量*/
temp=x>y?x:y;/*函數(shù)體的執(zhí)行部分*/
returntemp;/*返回函數(shù)值*/
}
函數(shù)返回類型函數(shù)名形式參數(shù)C語言中規(guī)定,函數(shù)不能嵌套定義。這個規(guī)定保證了每個函數(shù)都是一個相對獨立的程序模塊。在由多個函數(shù)組成的C程序中,各個函數(shù)的定義是并列的并且順序是任意的,函數(shù)在一個C程序中的定義順序與該C程序運行時函數(shù)的執(zhí)行順序無關(guān)。函數(shù)定義好后,就可以使用了,稱為函數(shù)的調(diào)用。C語言規(guī)定,在調(diào)用函數(shù)之前必須向系統(tǒng)描述所調(diào)用函數(shù)的基本特征,稱為函數(shù)的聲明。C語言中的函數(shù)分為標(biāo)準(zhǔn)庫函數(shù)和用戶自定義函數(shù)兩大類。下面分別介紹他們的聲明方法。
4.1.1函數(shù)的定義和聲明(1)標(biāo)準(zhǔn)庫函數(shù)的聲明方式#include<頭文件名>當(dāng)使用尖括號時,指定系統(tǒng)首先查找C編譯系統(tǒng)配置的頭文件路徑(include路徑);#include“頭文件名”當(dāng)使用雙引號時,指定系統(tǒng)首先查找當(dāng)前目錄。(2)用戶自定函數(shù)的聲明方式如果被調(diào)用函數(shù)(稱為被調(diào)函數(shù))與調(diào)用它的函數(shù)(稱為主調(diào)函數(shù))在同一源文件中,需要在函數(shù)調(diào)用之前對被調(diào)函數(shù)進(jìn)行聲明。形式為:
返回值類型函數(shù)名(形式參數(shù)表);4.1.1函數(shù)的定義和聲明/*Name:ex04-01.cpp*/#include<stdio.h>voidmain(){ longfactorial(intn); intnum; printf("Inputthenum:"); scanf("%d",&num); printf("%d!=%ld\n",num,factorial(num));}longfactorial(intn)//函數(shù)factorial的定義,最后沒有分號{ int i; longfact=1; for(i=1;i<=n;i++) fact*=i; returnfact;}函數(shù)聲明告訴編譯系統(tǒng)factorial是一個返回值是long,只有一個int參數(shù)的函數(shù),注意最后分號不可少!程序演示形式參數(shù)(簡稱形參)實際參數(shù)(簡稱實參)在上面程序中,主函數(shù)中的longfactorial(intn);語句就是對函數(shù)factorial的聲明。C程序中,對被調(diào)函數(shù)的聲明也可以書寫在主調(diào)函數(shù)定義之前,這種方式下函數(shù)聲明語句之后的所有函數(shù)都能對被聲明函數(shù)進(jìn)行調(diào)用,如下面的程序段所示:#include<stdio.h>longfactorial(intn);//對函數(shù)factorial的聲明,該程序中其它函數(shù)均可以調(diào)用它。voidmain(){ … }4.1.1函數(shù)的定義和聲明在函數(shù)的聲明語句中,形參的名字是無關(guān)緊要的(可以與函數(shù)定義中的不同甚至可以缺省),函數(shù)聲明語句的關(guān)鍵是形參的類型、個數(shù)和次序必須與定義對應(yīng)。例如上面對函數(shù)factorial的聲明語句還可以寫成為如下兩種形式:⑴longfactorial(int);/*對函數(shù)factorial的聲明中無形參名,表示默認(rèn)*/⑵longfactorial(intx);/*對函數(shù)factorial的聲明中形式參數(shù)名與函數(shù)定義不同,但意義一樣*/4.1.1函數(shù)的定義和聲明C語言規(guī)定在下列情況下可以不對被調(diào)函數(shù)進(jìn)行聲明:1.被調(diào)函數(shù)的返回值數(shù)據(jù)類型是整型或字符型在這種情況下,系統(tǒng)自動按整型進(jìn)行隱式聲明。但從現(xiàn)代程序設(shè)計技術(shù)的觀點出發(fā),對任何類型的函數(shù)在調(diào)用之前都必須聲明,所以許多較現(xiàn)代的C編譯系統(tǒng)在這種情況下仍然強(qiáng)制要求對被調(diào)函數(shù)進(jìn)行聲明。2.被調(diào)函數(shù)的定義出現(xiàn)在主調(diào)函數(shù)之前在這種情況下,系統(tǒng)在執(zhí)行程序中的函數(shù)調(diào)用語句之前已知道了被調(diào)函數(shù)的所有特征。
4.1.1函數(shù)的定義和聲明/*Name:ex04-02.cpp*/#include<stdio.h>longfactorial(intn)//函數(shù)factorial的定義出現(xiàn)在主調(diào)函數(shù)main的前面{ int i; longfact=1; for(i=1;i<=n;i++) fact*=i; returnfact;}voidmain() //主函數(shù)中沒有對函數(shù)factorial進(jìn)行聲明的語句{ intnum;//longfactorial(intn); printf("Inputthenum:"); scanf("%d",&num); printf("%d!=%ld\n",num,factorial(num));}程序演示4.1.1函數(shù)的定義和聲明什么是函數(shù)的調(diào)用?先看下列程序/*計算S=2!-3!+4!*/
#include<stdio.h>voidmain(){floatqsn(int
x);//函數(shù)的聲明
floats;s=qsn(2)-qsn(3)+qsn(4);/*直接調(diào)用qsn函數(shù)進(jìn)行計算*/printf(“S=%f\n”,s);}
floatqsn(intn)/*定義函數(shù)qsn為階乘*/{intk;floatqsn;qsn=1;for(k=1;k<=n;k++)qsn*=k;return(qsn);}4.1.2值參數(shù)傳遞的函數(shù)調(diào)用函數(shù)調(diào)用的一般形式為:
函數(shù)名(實際參數(shù)列表)C程序中對函數(shù)的調(diào)用方式有三種:
⑴函數(shù)語句方式 把函數(shù)調(diào)用作為一個語句來使用。例如:for(k=0;k<5);k++)printline();//函數(shù)調(diào)用部分,無參函數(shù)
4.1.2值參數(shù)傳遞的函數(shù)調(diào)用
⑵函數(shù)表達(dá)式方式 在函數(shù)調(diào)用的這種方式下,函數(shù)調(diào)用出現(xiàn)在一個表達(dá)式中,這個表達(dá)式亦稱為函數(shù)表達(dá)式。此時要求函數(shù)被調(diào)用后必須要返回一個確定的值以參加表達(dá)式運算。例如:
s=qsn(2)-qsn(3)+qsn(4);
⑶函數(shù)參數(shù)方式在函數(shù)調(diào)用的這種方式下,函數(shù)調(diào)用作為另外一個函數(shù)調(diào)用的實際參數(shù)出現(xiàn)。此時要求函數(shù)被調(diào)用后必須要返回一個確定的值以作為其外層函數(shù)調(diào)用的實際參數(shù)。例如:
4.1.2值參數(shù)傳遞的函數(shù)調(diào)用
/*函數(shù)參數(shù).cpp*/
#include<stdio.h>
intmax(intx,inty)/*函數(shù)定義*/{return(x>y?x:y);}main(){inta,b,c;/*定義3個整型變量,保存需要保存的三個數(shù)*/
intMAX;/*保存最大值*/
printf(“請輸入三個數(shù)\n”);
scanf(“%d,%d,%d”,&a,&b,&c);
MAX=max(a,max(b,c));/*求三個數(shù)的最大值,函數(shù)max作為實參*/
printf(“3個數(shù)中的最大值是:%d\n”,MAX);
}//n個數(shù)的最大值可以兩兩相求,例如4個數(shù)中的最大值max(max(a,b),max(c,d)4.1.2值參數(shù)傳遞的函數(shù)調(diào)用當(dāng)被調(diào)函數(shù)是有參函數(shù)時,函數(shù)的調(diào)用必然伴隨著參數(shù)傳遞。在C程序函數(shù)調(diào)用的數(shù)據(jù)傳遞中,傳遞的是實際參數(shù)所具有的值。當(dāng)實際參數(shù)是常量、變量或函數(shù)調(diào)用時,傳遞的數(shù)據(jù)就是這些數(shù)據(jù)對象所具有的內(nèi)容,這種方式亦稱為傳數(shù)據(jù)值方式。如果函數(shù)調(diào)用時所傳遞的實際參數(shù)是數(shù)據(jù)對象在內(nèi)存中存儲的首地址值,則稱之為傳地址值方式,對于指針參數(shù)和數(shù)組參數(shù)就是使用的傳地址值調(diào)用方式,將分別在本章的4.1.3和4.1.4小節(jié)中予以討論。4.1.2值參數(shù)傳遞的函數(shù)調(diào)用無論函數(shù)調(diào)用時的傳遞是數(shù)值值還是地址值,函數(shù)調(diào)用的執(zhí)行過程都可以分為下面四個步驟:(1)系統(tǒng)為被調(diào)函數(shù)中的局部變量分配存儲;(2)如果是有參函數(shù)調(diào)用則進(jìn)行參數(shù)傳遞,主調(diào)函數(shù)將實參值傳遞給被調(diào)函數(shù)的形參,傳遞時要保證參數(shù)的個數(shù)、類型、位置等一一對應(yīng);(3)程序執(zhí)行的控制流程轉(zhuǎn)移到被調(diào)函數(shù)執(zhí)行;(4)執(zhí)行完被調(diào)函數(shù)后,程序執(zhí)行的控制流程以及被調(diào)函數(shù)的執(zhí)行結(jié)果返回到主調(diào)函數(shù)中的調(diào)用點。4.1.2值參數(shù)傳遞的函數(shù)調(diào)用例4.3的程序討論函數(shù)調(diào)用的執(zhí)行過程,為了討論方便為程序加上行號。1 /*ex04-03.cpp*/2 #include<stdio.h>3 voidmain()4 { voidswap(intx,inty);5 inta=3,b=5;6 printf("swap調(diào)用前:a=%d,b=%d\n",a,b);7 swap(a,b);8 printf("swap調(diào)用后:a=%d,b=%d\n",a,b);9 }10 voidswap(intx,inty)11 { intt;12 t=x,x=y,y=t;13 printf("swap調(diào)用中:x=%d,y=%d\n",x,y);14 }4.1.2值參數(shù)傳遞的函數(shù)調(diào)用C程序執(zhí)行時,函數(shù)在被調(diào)用之前其形參和函數(shù)體中定義的普通變量在系統(tǒng)中都是不存在的,它們在系統(tǒng)中出現(xiàn)或消失與函數(shù)調(diào)用的過程有著密切的關(guān)系,在例4.3程序執(zhí)行到第7行之前,函數(shù)swap中的形參x和y以及函數(shù)體中定義的變量t在系統(tǒng)中均不存在,參見圖4.2a)。
4.1.2值參數(shù)傳遞的函數(shù)調(diào)用函數(shù)swap傳數(shù)據(jù)值調(diào)用的過程如下:(1)系統(tǒng)為被調(diào)函數(shù)中的局部變量分配存儲。如在例4.3程序中,程序執(zhí)行到第7行時系統(tǒng)才會創(chuàng)建變量x、y和t(即為這些變量分配存儲),參見圖4.2b)。4.1.2值參數(shù)傳遞的函數(shù)調(diào)用(2)參數(shù)傳遞。傳遞參數(shù)值實質(zhì)上是將實參的內(nèi)容拷貝給形參,一旦拷貝完成則實參與形參就沒有任何關(guān)系。在例4.3程序中,傳遞參數(shù)時將實參a的值拷貝給形參x,實參b的值拷貝給形參y,拷貝完成后實參變量a、b與形參變量x、y就斷開聯(lián)系,參見圖4.2c)。4.1.2值參數(shù)傳遞的函數(shù)調(diào)用(3)控制流程轉(zhuǎn)移到被調(diào)函數(shù)執(zhí)行。在例4.3程序中,參數(shù)調(diào)用完成后程序的控制流程(執(zhí)行順序)就從第7行轉(zhuǎn)移到第12行開始執(zhí)行函數(shù)swap,參見圖4.2c)d)e)。4.1.2值參數(shù)傳遞的函數(shù)調(diào)用(4)控制流程返回主調(diào)函數(shù)。在例4.3程序中,程序執(zhí)行到第14行時將控制流程返回到第7行的函數(shù)調(diào)用點后。與此同時,調(diào)用swap函數(shù)時創(chuàng)建的變量x、y和t都自動被系統(tǒng)撤消。程序控制流程執(zhí)行到被調(diào)函數(shù)中的return語句或函數(shù)體的函數(shù)的最后一個右花括號“}”時,將程序執(zhí)行的控制流程以及被調(diào)函數(shù)的執(zhí)行結(jié)果返回到主調(diào)函數(shù)中的調(diào)用點。特別需要注意的是,隨著程序控制流程的返回,系統(tǒng)會自動收回為被調(diào)函數(shù)的形式參數(shù)和局部變量分配的存儲單元,即在函數(shù)被調(diào)用時創(chuàng)建的形式參數(shù)和局部變量會自動撤銷。
4.1.2值參數(shù)傳遞的函數(shù)調(diào)用程序執(zhí)行的結(jié)果如下所示:swap調(diào)用前:a=3,b=5swap調(diào)用中:x=5,y=3swap調(diào)用后:a=3,b=5從輸出結(jié)果看到main中a和b的值并沒有交換,原因就是在調(diào)用函數(shù)時,只是把參數(shù)a和b的值拷貝給了x和y。在swap中,只是對局部變量x、y的值進(jìn)行了交換,并不影響主調(diào)函數(shù)中作為參數(shù)變量a和b。如果需要在被調(diào)函數(shù)中對主調(diào)函數(shù)中實際參數(shù)進(jìn)行操作,則需要將主調(diào)函數(shù)中實際參數(shù)在內(nèi)存中存放的地址起始值傳遞給被調(diào)函數(shù)對應(yīng)的形式參數(shù),這就是“傳地址調(diào)用”的方法。通過這種方法可以做到“函數(shù)中對參數(shù)的修改將影響主調(diào)函數(shù)中參數(shù)值”。這就好比在兩個函數(shù)間開了一個通道,讓一個函數(shù)可以操作另外一個函數(shù)的局部變量。程序演示4.1.2值參數(shù)傳遞的函數(shù)調(diào)用本小節(jié)主要討論指針變量的基本用法和實際參數(shù)值是地址值時的函數(shù)調(diào)用問題1)指針和指針變量的概念程序中的任何數(shù)據(jù)對象在運行過程中一旦被使用,就會對應(yīng)計算機(jī)系統(tǒng)內(nèi)存中的一個地址。由于系統(tǒng)內(nèi)存儲器是按字節(jié)編址的,一個數(shù)據(jù)對象有可能占用一至若干個字節(jié)的存儲單元,在程序設(shè)計語言中一般將數(shù)據(jù)對象的名字與其所占用的存儲單元的首地址相對應(yīng)。在計算機(jī)系統(tǒng)中,內(nèi)存單元的地址是用有序整型數(shù)進(jìn)行編址的,所以存儲系統(tǒng)的地址序號本質(zhì)上就是無符號的整型數(shù)據(jù)。4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用什么是指針?我們知道存放在內(nèi)存中的數(shù)據(jù)有些占4字節(jié)(整型),有些占8字節(jié),也就是說只要知道數(shù)據(jù)的起始位置和數(shù)據(jù)類型,計算機(jī)就可以準(zhǔn)確的訪問這個數(shù)據(jù)。把起始地址記為數(shù)據(jù)的“地址”,如圖:
4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用A19233.141596.02e+23101……105地址內(nèi)存空間10010110510911720502046char型,占1字節(jié)bint型,占4字節(jié)cfloat型,占4字節(jié)ddouble型,占8字節(jié)pq2046存放的是整數(shù)1923的地址p,2050存放的是浮點數(shù)3.14159的地址q,這就需要我們用一種新的數(shù)據(jù)表示存儲的數(shù)據(jù)是地址而不是其他的數(shù)值。在C中,除了在前面介紹的各種普通數(shù)據(jù)類型之外,還有另外一種特殊性質(zhì)的變量,即指針變量,簡稱指針。
指針是存放一個數(shù)據(jù)或變量地址的變量。它和普通變量一樣占用一定的存儲空間,不同之處在于:指針存儲的不是普通數(shù)據(jù),而是一個數(shù)據(jù)或變量的地址。怎么能知道一個變量的地址呢?
使用運算符&!假設(shè)定義一個整型指針變量,名字是p,定義x為普通整型變量,對x取地址,&x的值就是變量x的地址。4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用intx;int*p;/*變量前加*,表示該變量是指針變量*/p=&x;4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用變量x的地址被裝入指針p的存儲區(qū)域,即p的內(nèi)容就是變量x的地址。如圖示:變量x應(yīng)當(dāng)和指針變量p的類型保持一致。101指針p地址101變量x(a)將x的地址裝入p(b)指針p指向變量x指針p地址101變量xp=&x稱*p為指針p的目標(biāo)變量,也就是變量x,指針除了可以指向變量外,還可以指向內(nèi)存中其它任何一種數(shù)據(jù)結(jié)構(gòu),如數(shù)組、結(jié)構(gòu)體類型和函數(shù)等。指針的定義:例如: int*p,*y; 定義了兩個整型的指針變量p和y,注意指針變量是p和y,而不是*p和*y 如果有需要,指針變量也可以和同類型的普通變量混合定義。 例如: charch1,ch2,*p; 定義了兩個字符變量ch1、ch2以及一個指針變量4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用類型標(biāo)識符*指針名指針變量賦值的方法有兩種:一種是;(1)使用賦值號的一般形式為:
指針變量名=地址值;(2)另外一種是指針變量在定義時進(jìn)行初始化,一般形式為:
數(shù)據(jù)類型符*指針變量名=初始化地址值;
4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用
例如:intx,*y=&x;
/*定義了變量x和指針變量y,并將x的首地址賦值給y*/或
intx,*y; /*定義變量x和指針變量y*/ y=&x;/*將變量x的首地址賦值給指針變量y*/(假設(shè)x的值為100,X對應(yīng)的存儲單元的首地址是25000,則指針變量y和它指向的變量x之間的關(guān)系如圖)2500025000100xy10025000xya)b)圖4.3指針變量y與變量x的存儲關(guān)系圖4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用深入理解指針運算符*和地址運算符&如前例:*y是y所指向的變量,即x,*是一個指針運算符,y是運算對象,下列賦值語句結(jié)果一樣:
對整型變量x取地址,下列操作等同*(&x)表示的含義呢?表示x本身4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用x=8;或*y=8;&x或&(*y)在C程序設(shè)計中,對于指針變量的理解和使用時還應(yīng)該特別注意以下幾點:⑴在指針變量的定義形式中,星號(*)只是一個標(biāo)志,表示其后面的變量是指針變量。例如,在指針變量定義語句intx,*y;中,y是指針變量。⑵一個指針變量只能指向與它同類型的普通變量,即只有數(shù)據(jù)類型相同時普通變量才能將自己存儲單元的首地址賦值給指針變量,其原因是不同類型的變量所占存儲單元的字節(jié)數(shù)是不同的。intx;float*ptr;ptr=&x;/*錯誤,指針變量沒有指向合適的數(shù)據(jù)對象*/但在這種情況下有一個特例,可以將任何數(shù)據(jù)類型對象的存儲首地址賦值給void類型(空類型)的指針變量,例如:intx; void*p=&x; /*將整型變量x的存儲首地址賦值給空類型指針變量p*/4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用⑶指針變量只能在有確定的指向后才能正常使用,也就是說指針變量中必須要有確定的地址。沒有確定指向的指針稱為“空指針”或稱為“懸掛指針”,使用這種指針變量有可能引起不可預(yù)知的錯誤。⑷指針變量中只能存放地址值,不能把除NULL外的整型常數(shù)直接賦給指針變量。例如,下面的指針變量的賦值是錯誤的:int*ptr;ptr=100;/*錯誤,整型常數(shù)值直接賦給指針變量*/
以下是正確的:
float*p=NULL;/*定義實型指針變量p并將其初始化為常量NULL*/ 或者float*p;
/*定義實型指針變量p*/
p=NULL;
/*將符號常量NULL賦值給指針變量p*/4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用3.指針變量的引用怎樣用指針運算符來處理數(shù)據(jù)呢?設(shè)變量x、y同類型,把x復(fù)制到y(tǒng)中,可采取下列方法:直接賦值: y=x;使用指針:設(shè)指針變量p用于保存x的地址: p=&x;再執(zhí)行語句 y=*p;例如下列程序段
intx=20,y,*ptr;ptr=&x;y=*ptr;該程序段的意思為:定義整型變量x(初值為20),y和指針*ptr,將變量x的地址賦給指針變量ptr,然后以指針變量ptr的值為內(nèi)存單元地址,將該單元的數(shù)據(jù)取出賦給變量y,相當(dāng)于語句y=x;例如有語句序列為:
intx,*y;y=&x;此時&x等價于y,而*y則等價于變量x。在這種情況下,有下面的等價關(guān)系的:scanf("%d",&x);等價于scanf("%d",y);printf(“%d\n”,x);等價于printf("%d\n",*y);在編寫程序過程,注意的幾個概念:設(shè)有指針變量pP指針變量,它的內(nèi)容是地址值。*p指針的目標(biāo)變量,它的內(nèi)容是數(shù)據(jù)(p地址的數(shù)據(jù))。&p指針變量的地址,即“地址的地址”。NULL空指針,表示指針內(nèi)容為0p=NULL;/*等價于p=0*/4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用例4.4取地址運算符(&)和指針運算符(*)的使用示例。/*Name:ex04-04.cpp*/#include<stdio.h>voidmain(){ intx=200,*y; y=&x; *y=300; printf("%x:%d,%d\n",y,x,*y);}程序執(zhí)行的結(jié)果為:13ff7c:300,300(注意變量y的十六進(jìn)制值在不同的機(jī)器上可能是不同的)。程序演示4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用以16進(jìn)制形式輸出無符號整數(shù)4.地址值參數(shù)傳遞調(diào)用
函數(shù)調(diào)用時如果被調(diào)函數(shù)的形參用指針型參數(shù)(即某種數(shù)據(jù)類型的指針變量作為函數(shù)的形式參數(shù)),則主調(diào)函數(shù)中的實參就必須是指針值(地址)。這種在函數(shù)調(diào)用過程中傳遞主調(diào)函數(shù)實際參數(shù)的指針(即實際參數(shù)存儲單元的首地址)的方式提供了在被調(diào)函數(shù)中操作主調(diào)函數(shù)中實際參數(shù)的可能性。4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用例4.5地址值參數(shù)傳遞函數(shù)調(diào)用示例。/*Name:ex04-05.cpp*/#include<stdio.h>voidmain(){ voidswap(int*x,int*y);/*函數(shù)聲明中形參是指針變量*/ inta=3,b=5; printf("swap函數(shù)調(diào)用前:a=%d,b=%d\n",a,b);
swap(&a,&b);/*實參也必須為地址*/ printf("swap函數(shù)調(diào)用后:a=%d,b=%d\n",a,b);}voidswap(int*x,int*y)/*函數(shù)定義中形參是指針變量*/{ intt;/*注意t不是指針變量,是被指針指向的變量*/ t=*x; *x=*y; *y=t;}4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用執(zhí)行過程如圖:3510002000xyabta)參數(shù)傳遞過程中3510002000xyabtb)參數(shù)傳遞完成后35100020003xyabtc)t=*x執(zhí)行后55100020003xyabtd)*x=*y執(zhí)行后53100020003xyabte)*y=t執(zhí)行后圖4.4地址值傳遞函數(shù)調(diào)用時參數(shù)的變化情況4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用程序執(zhí)行后的輸出結(jié)果為:swap函數(shù)調(diào)用前:a=3,b=5swap函數(shù)調(diào)用后:a=5,b=3從上面程序執(zhí)行的過程可以得出使用地址傳送方式在函數(shù)之間傳遞數(shù)據(jù)的特點是:數(shù)據(jù)在主調(diào)函數(shù)和被調(diào)函數(shù)中均使用同一存儲單元,所以在被調(diào)函數(shù)中對形參數(shù)據(jù)任何的變動必然會反映到主調(diào)函數(shù)中來。5.指針變量與被指針指向變量的區(qū)別從上面程序執(zhí)行的過程t=*x;*x=*y;*y=t;可以看出在操作的對象是被指針指向變量。而下面這個例子是交換的對象是指針變量本身,體會它們的不同之處。程序演示4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用例4.6地址值參數(shù)傳遞函數(shù)調(diào)用示例。/*Name:ex04-06.cpp*/#include<stdio.h>voidmain(){ voidswap(int*x,int*y); inta=3,b=5; printf("swap函數(shù)調(diào)用前:a=%d,b=%d\n",a,b); swap(&a,&b); printf("swap函數(shù)調(diào)用后:a=%d,b=%d\n",a,b);}voidswap(int*x,int*y){ int*t;/*t是指針變量*/ t=x; x=y; y=t;}4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用執(zhí)行過程如圖:3510002000xyabta)參數(shù)傳遞過程中3510002000xyabtb)參數(shù)傳遞完成后35100020001000xyabtc)t=x執(zhí)行后35200020001000xyabtd)x=y執(zhí)行后35200010001000xyabte)y=t執(zhí)行后圖4.5地址值傳遞函數(shù)調(diào)用時參數(shù)的變化情況4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用程序執(zhí)行的結(jié)果并沒使得主函數(shù)中的實參變量a和b交換內(nèi)容。程序執(zhí)行的結(jié)果為:swap函數(shù)調(diào)用前:a=3,b=5swap函數(shù)調(diào)用后:a=3,b=5程序演示4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用雖然在被調(diào)用函數(shù)中使用指針型參數(shù)就提供了在被調(diào)函數(shù)中操作主調(diào)函數(shù)中實際參數(shù)的可能性。但并不是用了指針變量作函數(shù)的形式參數(shù)就一定可以在被調(diào)函數(shù)中操作或修改主調(diào)函數(shù)中的實參。在被調(diào)函數(shù)中是否能夠操作或修改主調(diào)函數(shù)中實參值還要取決于在被調(diào)函數(shù)中對指針形參的操作方式,操作指針形參變量指向的對象(即實參本身)則可以達(dá)到在被調(diào)函數(shù)中操作或修改主調(diào)函數(shù)實參的目的;但若操作的是指針形參變量本身則不能實現(xiàn)在被調(diào)函數(shù)中操作或修改主調(diào)函數(shù)實際參數(shù)的目的。4.1.3指針基本概念和地址值參數(shù)傳遞函數(shù)調(diào)用
在C程序設(shè)計中,既可以用數(shù)組的元素作為函數(shù)的參數(shù),也可以將數(shù)組看成一個整體作為函數(shù)的參數(shù)。
使用數(shù)組元素作為參數(shù)傳遞,其用法都與普通變量用法一樣,實現(xiàn)的是函數(shù)間的傳值調(diào)用。而用數(shù)組名作為實參,是傳遞的是數(shù)組地址。
例如4.7程序在執(zhí)行中,對于主函數(shù)中傳遞過來的一維數(shù)組a和二維數(shù)組b的每一個數(shù)組元素,利用自定義函數(shù)myprint進(jìn)行輸出。4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用程序演示/*Name:ex04-07.cpp*/#include<stdio.h>#include<stdlib.h>#include<time.h>#defineN5voidmain(){ voidmyprint(intx); inta[N],b[N][N],i,j; srand(time(NULL)); printf("下面是數(shù)組a的數(shù)據(jù)...\n"); for(i=0;i<N;i++) { a[i]=rand()%100;
myprint(a[i]); }printf("\n下面是數(shù)組b的數(shù)據(jù)...\n");for(i=0;i<N;i++){for(j=0;j<N;j++){b[i][j]=rand()%100;
myprint(b[i][j]);}printf("\n");
}}voidmyprint(intx){ printf("%4d",x);}4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用將數(shù)組看成一個整體作為函數(shù)參數(shù)時,用數(shù)組名作為函數(shù)的形式參數(shù)或?qū)嶋H參數(shù),實現(xiàn)的是函數(shù)間的傳地址值調(diào)用。先認(rèn)識指針與數(shù)組的關(guān)系:intarray[9],*ptr;ptr=array;或者intarray[9],*ptr;ptr=&array[0]/*表示指針ptr指向了數(shù)組array的首地址*/補(bǔ)充:為何數(shù)組名是一個(首)地址ptrarrayarray[0]*ptrarray[i]array[1]array[2]array[3]*(ptr+1)*(ptr+2)*(ptr+3)*(ptr+i)如圖:數(shù)組與指針的關(guān)系,當(dāng)數(shù)組名傳給函數(shù)時,傳送的是數(shù)組的起始位置
1.一維數(shù)組名作為函數(shù)參數(shù)實現(xiàn)的是“傳地址值調(diào)用”,其本質(zhì)是將它的全部存儲區(qū)域或者部分存儲區(qū)域提供給形式參數(shù)數(shù)組共享,即形參數(shù)組與實參數(shù)組是同一存儲區(qū)域或者形參數(shù)組是實參數(shù)組存儲區(qū)域的一部分。存儲關(guān)系如下圖:實參數(shù)組a…形參數(shù)組b[]注:形參數(shù)組b本質(zhì)上是指針變量圖4.6數(shù)組存儲區(qū)域全部共享時形參數(shù)組與實參數(shù)組的關(guān)系4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用需要把實參數(shù)組中從某個元素值后的部分傳遞給被調(diào)函數(shù)中的形參數(shù)組,則使用實參數(shù)組某個元素的地址(參見圖4.7)。
實參數(shù)組&a[2]…形參數(shù)組b[]注:形參數(shù)組b本質(zhì)上是指針變量圖4.7數(shù)組存儲區(qū)域部分共享時形參數(shù)組與實參數(shù)組的關(guān)系4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用例4.8編制求和函數(shù)并通過該函數(shù)求數(shù)組的元素值和。intsum(intv[],intn){ inti,s=0; for(i=0;i<n;i++) s+=v[i]; returns;}/*Name:ex04-08.cpp*/#include<stdio.h>#defineN10voidmain(){intsum(intv[],intn);/*v表示一維數(shù)組,n表示其長度*/inta[N]={1,2,3,4,5,6,7,8,9,10},total;total=sum(a,N);/*調(diào)用時將數(shù)組名a作為實參傳遞給形參v,首地址默認(rèn)為0,也可以寫作&a[0]*/printf("total=%ld\n",total);}程序演示4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用例4.9編制求和函數(shù)并通過該函數(shù)求數(shù)組自某一元素后的所有元素值和,起始點元素序號從鍵盤上輸入。/*Name:ex04-09.cpp*/#include<stdio.h>#defineN10voidmain(){intsum(intv[],intn);inta[N]={1,2,3,4,5,6,7,8,9,10},total,pos;printf("請輸入求和起始元素序號:");scanf("%d",&pos);
total=sum(&a[pos],N-pos);/*調(diào)用時將數(shù)組a[pos]作為實參首地址傳遞給形參v,*/printf("total=%ld\n",total);}intsum(intv[],intn){ inti,s=0; for(i=0;i<n;i++) s+=v[i]; returns;}程序演示4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用比較例4.8和例4.9的程序,可以發(fā)現(xiàn)函數(shù)sum沒有任何改變,程序中有所改變的是主調(diào)函數(shù)中的調(diào)用表達(dá)式:sum(&a[pos],N-pos),其中,參數(shù)&a[pos]表示將數(shù)組a自a[pos]元素以后的元素全部提供給形參數(shù)組共享,N-pos是傳遞到函數(shù)total中共享的數(shù)組元素個數(shù)。4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用2.二維數(shù)組作函數(shù)的參數(shù)
數(shù)組a的起始地址數(shù)組的起始地址表示方法a 表示平面的起始地址(二級地址)&a[0][0]表示線性的起始地址(一級地址)a[0]表示線性的起始地址(一級地址)*a表示線性的起始地址(一級地址)圖4.8二維數(shù)組起始地址的表示方法示意
二維數(shù)組在存儲時也是有序地占用一片連續(xù)的內(nèi)存區(qū)域,數(shù)組的名字表示這段存儲區(qū)域的首地址。需要特別注意的是,二維數(shù)組起始地址有多種表示方法,而且這些表示方法在物理含義上還有表示平面起始地址和表示線性起始地址之分,所以在使用二維數(shù)組的起始地址使必須注意區(qū)分需要用哪一種起始地址。
4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用(1)用二維數(shù)組名字作為實際參數(shù)實參用a,形參用b[][5]圖4.9實際參數(shù)為二維數(shù)組名字,用二維數(shù)組名作為函數(shù)參數(shù)實現(xiàn)的是“傳地址值調(diào)用”,其本質(zhì)仍然是在函數(shù)調(diào)用期間實際參數(shù)數(shù)組將它的全部存儲區(qū)域提供給形式參數(shù)數(shù)組共享,即形參數(shù)組與實參數(shù)組是同一存儲區(qū)域。實參用a形參用b[][5]圖4.9實際參數(shù)為二維數(shù)組名字4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用用二維數(shù)組名字作為實際參數(shù)傳地址調(diào)用例4.10編制求二維矩陣最大元素的函數(shù)(假定矩陣為3行4列),用相應(yīng)主函數(shù)進(jìn)行測試。/*Name:ex04-10.cpp*/#include<stdio.h>#defineM3#defineN4voidmain(){ intmax(intv[][N]); inta[M][N]={38,23,56,9,56,2,789,45,76,7,45,34}; printf("Maxvalueis:%d\n",max(a));}程序演示4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用intmax(intv[][N]) //注意數(shù)組參數(shù)只能省略最高維的長度指定{ inti,j,maxv; maxv=v[0][0]; for(i=0;i<M;i++) for(j=0;j<N;j++) if(v[i][j]>maxv) maxv=v[i][j]; returnmaxv;}程序演示4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用例4.10程序的函數(shù)max中使用了二維數(shù)組樣式的形式參數(shù)接收從主調(diào)函數(shù)中傳遞過來的二維數(shù)組首地址,使得形參數(shù)組v共享實參數(shù)組a的存儲區(qū)域;然后通過對形參數(shù)組v的操作達(dá)到操作是參數(shù)a的目的,即在形參數(shù)數(shù)組v中尋找最大值實質(zhì)上是在實參數(shù)組a中尋找最大值,程序執(zhí)行的結(jié)果為:Maxvalueis:789。該函數(shù)雖然能求出矩陣的最大元素,但有一個致命弱點,它只能求出3行4列矩陣的最大元素。其根本原因是:使用高維數(shù)組名作函數(shù)的形參時,必須要制定除最高維以外的所有維的長度。為編制較通用的函數(shù),可以使用一維數(shù)組名作形參,此時,可以不制定長度,但仍然要注意一下2點:(1)調(diào)用時的實參必須是一維數(shù)組的地址(指針)形式。如圖4.8的一級地址。(2)在函數(shù)中每一行的長度由程序員自己控制,應(yīng)該從主調(diào)函數(shù)中傳遞過來。4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用(2)用二維數(shù)組起始地址的一級地址形式作為實際參數(shù)使用一維數(shù)組樣式的形式參數(shù)接收二維數(shù)組實參,數(shù)組存儲區(qū)域全部共享或部分共享時形參數(shù)組與實參數(shù)組的關(guān)系如圖4.10所示。
4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用實參數(shù)組a…形參數(shù)組b[]注:形參數(shù)組b本質(zhì)上是指針變量圖4.6數(shù)組存儲區(qū)域全部共享時形參數(shù)組與實參數(shù)組的關(guān)系例4.11重新編制例4.10中的函數(shù)max,使其能夠處理任意行列的二維數(shù)組。/*Name:ex04-11.cpp*/#include<stdio.h>#defineM3#defineN4voidmain(){ intmax(intv[],intm,intn); inta[M][N]={38,23,56,9,56,2,789,45,76,7,45,34}; printf("Maxvalueis:%d\n",max(a[0],M,N));}4.1.4數(shù)組參數(shù)傳遞函數(shù)調(diào)用intmax(intv[],intm,intn){ inti,j,maxv; maxv=v[0]; for(i=0;i<m;i++) for(j=0;j<n;j++) if(v[i*n+j]>maxv) maxv=v[i*n+j]; returnmaxv;}程序中函數(shù)max用一維數(shù)組形參v來接收從主調(diào)函數(shù)中傳遞過來的二維數(shù)組首地址,注意到二維數(shù)組的名字表示的是二級地址,所以被傳遞的二維數(shù)組的首地址不能直接用二維數(shù)組名表示而應(yīng)該使用3種一級地址形式,本示例中使用的是a[0],還可以使用&a[0][0]和*a形式。在被調(diào)函數(shù)中將傳遞過來的二維數(shù)組當(dāng)作一維數(shù)組處理,其元素對應(yīng)關(guān)系應(yīng)該是:a[i][j]→v[i*n+j]。程序執(zhí)行的結(jié)果為:Maxvalueis:789。程序演示函數(shù)的定義和調(diào)用函數(shù)的嵌套調(diào)用和遞歸調(diào)用變量的作用域和生存期編譯預(yù)處理多源文件C程序的組織方法第4章函數(shù)與程序結(jié)構(gòu)4.2.1函數(shù)的嵌套調(diào)用在C程序中函數(shù)不能嵌套定義。但C語言允許函數(shù)嵌套調(diào)用,所謂函數(shù)的嵌套調(diào)用就是一個函數(shù)在自己被調(diào)用的過程中又調(diào)用了另外的函數(shù)。一個兩層嵌套函數(shù)調(diào)用的過程如圖4.10。主函數(shù)函數(shù)fun1函數(shù)fun2程序運行結(jié)束調(diào)用函數(shù)fun1調(diào)用函數(shù)fun2fun1執(zhí)行結(jié)束fun2執(zhí)行結(jié)束圖4.10兩層函數(shù)嵌套調(diào)用示意圖4.2函數(shù)的嵌套調(diào)用和遞歸調(diào)用例4.12編程序計算要求對n項的求和以及每一項ik的計算都用獨立的函數(shù)實現(xiàn),k和n的值在主函數(shù)中從鍵盤輸入。程序設(shè)計思路:可以把問題分解為兩個模塊:求冪次方模塊和求和模塊,在求和模塊中要包含求冪模塊。f1()函數(shù)的參數(shù)為n和k,其返回值是n的k次方。f2()函數(shù)的參數(shù)為n和k,返回值是冪次方的累加和。4.2.1函數(shù)的嵌套調(diào)用#include<stdio.h>longf1(intn,intk)
{longpower=n;inti;for(i=1;i<k;i++)power*=n;returnpower;}longf2(intn,intk) {longsum=0;inti;for(i=1;i<=n;i++)sum+=f1(i,k);returnsum;}voidmain(){intn,k;
scanf(“%d,%d”,&n,&k);printf("Sumof%dpowersofintegersfrom1to%d=",k,n);printf("%d\n",f2(n,k));}輸入:4,5結(jié)果:Sumof5powersofintegersfrom1to4=1300該程序中,main()中調(diào)用了f2()函數(shù),而f2()函數(shù)中又調(diào)用了f1()函數(shù),這就是函數(shù)的嵌套調(diào)用。程序演示4.2.1函數(shù)的嵌套調(diào)用
一個函數(shù)直接地或間接地自己調(diào)用自己,稱為函數(shù)的遞歸調(diào)用。特點為:(1)遞歸調(diào)用中每次嵌套調(diào)用的函數(shù)都是該函數(shù)本身;(2)遞歸調(diào)用不會無限制進(jìn)行下去,即這種特殊的自己對自己的嵌套調(diào)用總會在某種條件下結(jié)束。(3)遵從”先進(jìn)后出“規(guī)則。遞歸調(diào)用的實現(xiàn)依靠系統(tǒng)提供一個特殊部件(堆棧)存放未完成的操作。計算機(jī)系統(tǒng)的堆棧是一段先進(jìn)后出(FILO)的存儲區(qū)域,系統(tǒng)在遞歸調(diào)用時將在遞歸過程中應(yīng)該執(zhí)行而未執(zhí)行的操作依次從堆棧棧底開始存放,當(dāng)遞歸結(jié)束回溯時再依存放時相反的順序?qū)⑺鼈儚亩褩V腥〕鰜韴?zhí)行,在壓棧和出棧操作中,系統(tǒng)使用堆棧指針指示出應(yīng)該存入和取出數(shù)據(jù)的位置。4.2.2函數(shù)的遞歸調(diào)用例4.13函數(shù)遞歸調(diào)用示例(使用遞歸調(diào)的方法反向輸出字符串)。1 /*Name:ex04-13.cpp*/2 #include<stdio.h>3 voidmain()4 { voidreverse();5 printf("輸入一個字符串,以'#'作為結(jié)束字符:");6 reverse();7 printf("\n");8 }9 voidreverse()10 { charch;11 ch=getchar();12 if(ch=='#')13 putchar(ch);14 else15 { reverse();16 putchar(ch);17 }18 }執(zhí)行如下圖4.2.2函數(shù)的遞歸調(diào)用所以程序執(zhí)行時輸入數(shù)據(jù)為字符串:abc#,則輸出數(shù)據(jù)為字符串:#cba。topa)空棧topputchar(‘a(chǎn)’)topc)putchar(‘a(chǎn)’)putchar(‘b’)topd)
putchar(‘a(chǎn)’)putchar(‘b’)putchar(‘c’)tope)
putchar(‘a(chǎn)’)putchar(‘b’)topputchar(‘a(chǎn)’)b)
f)
topg)空棧圖4.11遞歸調(diào)用時系統(tǒng)堆棧數(shù)據(jù)的變化示意圖程序演示4.2.2函數(shù)的遞歸調(diào)用例4.14編程序使用遞歸方式求n!。/*Name:ex04-14.cpp*/#include<stdio.h>voidmain(){ longfac(longn); longn,result; printf("Inputthen:"); scanf("%ld",&n);
result=fac(n); printf("%ld!=%ld\n",n,result);}longfac(longn){ if(n<=1) return1; else returnfac(n-1)*n;}4.2.2函數(shù)的遞歸調(diào)用fac(5)等于120執(zhí)行如下:程序演示4.2.2函數(shù)的遞歸調(diào)用fac(4)*5fac(3)*4*5fac(2)*3*4*5fac(1)*2*3*4*5調(diào)用返回fac(5)fac(5)等于120fac(5)→fac(4)*5fac(4)→fac(3)*4fac(3)→fac(2)*3fac(2)→fac(1)*2fac(1)→1遞歸壓棧方向fac(2)→fac(1)*2→1*2→2fac(3)→fac(2)*3→2*3→6fac(4)→fac(3)*4→6*4→24fac(5)→fac(4)*5→24*5→120遞歸回溯方向圖4.12函數(shù)遞歸調(diào)用過程示意圖執(zhí)行如下:程序演示4.2.2函數(shù)的遞歸調(diào)用
遞歸是程序設(shè)計中一種非常重要的技術(shù),與程序設(shè)計中其它控制方法策略相比較,遞歸程序設(shè)計的難度在于遞歸在人類社會的現(xiàn)實生活中沒有直接對應(yīng)的概念存在,而必須通過推理分析才能理解遞歸思想進(jìn)而實現(xiàn)遞歸程序設(shè)計。在實際設(shè)計遞歸函數(shù)程序時,我們可以將重點放在分析遞推公式和遞歸終止條件上,可以忽略系統(tǒng)的具體執(zhí)行過程,只要算法和遞推公式正確,結(jié)論一定是正確的。4.2.3遞歸函數(shù)設(shè)計初步(*)遞歸的實質(zhì)是一種簡化復(fù)雜問題求解的方法,它將問題逐步簡化直至趨于已知條件。在簡化的過程中必須保證問題的性質(zhì)不發(fā)生變化,即在簡化的過程中必須保證兩點:一是問題簡化后具有同樣的形式;二是問題簡化后必須趨于比原問題簡單一些。具體使用遞歸技術(shù)時,必須能夠?qū)栴}簡化分解為遞歸方程(即問題的形式)和遞歸結(jié)束條件(即最簡單的解)兩個部分。如例4.14求n的階乘,可以分解得到遞歸方程:n*(n-1)!和遞歸結(jié)束條件:n<=1時階乘為1。4.2.3遞歸函數(shù)設(shè)計初步(*)例4.15求菲波拉契數(shù)列。已知一對小兔出生一個月后變成一對成兔,兩個月后這對成兔就會生出一對小兔,三個月后這對成兔將生出第二對小兔,而第一對小兔又長大變成一對成兔,即一月成熟,二月生育,如此類推。請用計算機(jī)求解一對小兔經(jīng)n月后將繁衍成多少對兔子?4.2.3遞歸函數(shù)設(shè)計初步(*)可以分析出如下遞歸關(guān)系:按照上面分析得到的遞歸方程和結(jié)束條件,求菲波拉契數(shù)列的遞歸算法可以設(shè)計為如圖4.13所示。n=0或者n=1fib(n)retuan1returnfib(n-1)+fib(n-2)TF圖4.13菲波拉契數(shù)列的遞歸算法4.2.3遞歸函數(shù)設(shè)計初步(*)/*Name:ex04-15.cpp*/#include<stdio.h>voidmain(){ intm; floatfib(intn); printf("請輸入月份數(shù)"); scanf("%d",&m); printf("經(jīng)過%d個月后,兔子有%.0f對。\n",m,fib(m));}floatfib(intn){ if(n==0||n==1) return1; else returnfib(n-1)+fib(n-2);}程序演示4.2.3遞歸函數(shù)設(shè)計初步(*)
例4.16編程序用遞歸方法求兩個正整數(shù)的最大公約數(shù)。 可以分析得出如下遞歸關(guān)系:r=m%n=0gcd(n)retuannreturn
gcd(n,r)TF圖4.14最大公約數(shù)的遞歸算法4.2.2函數(shù)的遞歸調(diào)4.2.3遞歸函數(shù)設(shè)計初步(*)/*Name:ex04-16.cpp*/#include<stdio.h>voidmain(){ intGcd(intm,intn); intnum1,num2; printf("請輸入兩個正整數(shù):"); scanf("%d,%d",&num1,&num2); if(num1<num2) num1=num1+num2,num2=num1-num2,num1=num1-num2; printf("%d與%d的最大公約數(shù)是:%d\n",num1,num2,Gcd(num1,num2));}intGcd(intm,intn){ intr; if((r=m%n)==0) returnn; else returnGcd(n,r);}程序演示4.2.3遞歸函數(shù)設(shè)計初步(*)通過上面兩個示例的分析,遞歸方式的實現(xiàn)也是基于語言的條件控制結(jié)構(gòu)。遞歸函數(shù)設(shè)計的基本框架是相對固定的,其一般形式可以描述如下:if遞歸結(jié)束條件成立Return已知結(jié)果else將問題轉(zhuǎn)化為同性質(zhì)的較簡單子問題;以遞歸方式求解子問題(遞歸方程);4.2.3遞歸函數(shù)設(shè)計初步(*)例4.17漢諾塔(TowerofHanoi)問題。有A、B、C三根桿,最左邊桿上自下而上、由大到小順序串有64個金盤呈一塔形?,F(xiàn)要把左邊A桿上的金盤全部移到右邊C桿上,條件是一次只能移動一個盤,且不允許大盤壓在小盤的上面??梢詫h諾塔問題分解為下面三步遞歸求解:第一步:把a(bǔ)桿上的n-1個盤子設(shè)法借助b桿放到c桿,記做hanoi(n-1,a,c,b);第二步:把第n個盤子從a桿移動到b桿;第三步:把c桿上的n-1個盤子借助a桿移動到b桿,記做hanoi(n-1,c,b,a);4.2.3遞歸函數(shù)設(shè)計初步(*)/*Name:ex04-17.cpp*/#include<stdio.h>voidmain(){intn; voidhanoi(intn,chara,charb,charc); printf("\nPleaseinputthenumberofdiskstobemoved:"); scanf("%d",&n); hanoi(n,'a','b','c');}voidhanoi(intn,chara,charb,charc){ if(n==1) printf("\nMovedisc%dfrompile%cto%c",n,a,b); else { hanoi(n-1,a,c,b); printf("\nMovedisc%dfrompile%cto%c",n,a,b); hanoi(n-1,c,b,a); }}程序演示4.2.3遞歸函數(shù)設(shè)計初步(*)函數(shù)的定義和調(diào)用函數(shù)的嵌套調(diào)用和遞歸調(diào)用變量的作用域和生存期編譯預(yù)處理多源文件C程序的組織方法第4章函數(shù)與程序結(jié)構(gòu)
變量的作用域是指其有效范圍。變量的生存期是指變量存在的時間。⑴一個變量在某個函數(shù)、某個源程序文件或某幾個源程序文件范圍內(nèi)是有效的,則稱其有效的范圍為該變量的作用域,在此范圍內(nèi)可以訪問或引用該變量。如果從變量的作用域角度來考慮,變量可分為全局變量和局部變量。⑵一個變量的值在某一時刻是存在的,則認(rèn)為這一時刻屬于該變量的“生存期”,或稱其在此時刻“存在”。如果從變量的生存期來考慮,變量可分為靜態(tài)存儲變量和動態(tài)存儲變量。4.3變量的作用域和生存期從變量作用域概念上將變量分為“全局變量”和“局部變量”兩類。C語言使用extern和auto來表示變量的作用域?qū)傩浴?.全局變量指在所有函數(shù)之外定義的變量,全局變量也稱為外部變量。其作用域(作用范圍)是從定義位置開始直到本源文件結(jié)束為止。定義的一般形式如下:
[extern]<數(shù)據(jù)類型符>變量表;定義全局變量時一般將extern省略。使用extern,目的是對程序中定義的全局變量進(jìn)行重新聲明,這種聲明方法的意義和使用方法牽涉到多源程序文件,將在4.5節(jié)中予以討論。在定義全局變量時,也可以對其進(jìn)行初始化工作。如果在定義全局變量時沒有顯式初始化,C的編譯系統(tǒng)會自動將其初始化為0(若是字符類數(shù)據(jù)則初始化為’\0’)。4.3.1變量的作用域例4.18全局變量的作用域示例 /*Name:ex04-18.cpp*/2 #include<stdio.h>3 voidincrea();4 voidincreb();5 intx;6 voidmain()7 { x++;8 increa();9 increb();10 printf("x=%d\n",x); 11 }12 voidincrea()13 {14 x+=5;15 }16 voidincreb()17 {18 x-=2;19 }
程序在第5行定義了整型變量,由于變量x定義在所有函數(shù)的外面,所以變量x是全局變量,其作用范圍(作用域)從第5行開始至第19行結(jié)束。同時由于在定義全局變量x時沒有對其顯式初始化。該程序運行的結(jié)果為:x=4。
程序演示4.3.1變量的作用域
全局變量X的作用范圍例全局變量的作用域示例 #include<stdio.h>charc1,c2;/*全局變量*/charf1(inta,intb)/*定義函數(shù)f1*/{inti,j;……}inta=5,b=10;/*全局變量*/intf2(intp)/*定義函數(shù)f2*/{intx=a,y=b;……}main()/*主函數(shù)*/{floata,b;/*ab為主函數(shù)內(nèi)的局部變量*/……}
c1、c2、a、b、雖然都是全局變量,但作用的范圍不一樣。c1、c2在開始定義,所以在f1(),f2(),主函數(shù)內(nèi)都有效,而a、僅僅在f2(),主函數(shù)內(nèi)都有效。在主函數(shù)中又定義了2個局部變量a、b。這時全局變量不起作用。即當(dāng)全局變量和局部變量同名時,在局部變量作用范圍內(nèi),全局變量不起作用。4.3.1變量的作用域全局變量ab的作用范圍全局變量c1c2的作用范圍例全局變量的使用實例 #include<stdio.h>intx=3,y=5;intmax(inta,intb){return(a>b?a:b);}main(){intx=8;printf(“max(%d,%d)=%d\n”,x,y,max(x,y));}輸出結(jié)果:max(8,5)=8思考:主函數(shù)中如果沒有intx=8,結(jié)果是多少?如果全局變量x=-3,結(jié)果又是多少?如果要將全局變量作用域擴(kuò)展到其他源文件,在變量定義前加exterm。例如extermintx=3,y=5;4.3.1變量的作用域2.局部變量指定義在函數(shù)內(nèi)部的變量,也稱為自動變量。局部變量的作用域有三個,它們是:①函數(shù)形式參數(shù)表部分;②函數(shù)體內(nèi)部;③復(fù)合語句內(nèi)部{}。一般形式:[auto]<數(shù)據(jù)類型符>變量表;auto類型符是局部變量默認(rèn)的存儲類型,一般省略。如果局部變量在定義時沒有對其進(jìn)行顯式的初始化則其初始值是隨機(jī)的數(shù)值。局部變量的建立和撤消都是系統(tǒng)自動進(jìn)行的,如在某個函數(shù)中定義了自動變量,只有當(dāng)這個函數(shù)被調(diào)用時系統(tǒng)才會為這些局部變量分配存儲單元;當(dāng)函數(shù)執(zhí)行完畢,程序控制流程離開這個函數(shù)時,自動變量被系統(tǒng)自動撤銷,其所占據(jù)的存儲單元被系統(tǒng)自動收回。4.3.1變量的作用域例4.19局部變量在函數(shù)調(diào)用時的特征示例。/*Name:ex04-19.cpp*/#include<stdio.h>voidmain(){ intincre(); printf("x=%d\n",incre()); printf("x=%d\n",incre());}intincre(){intx=20; x+=5; returnx;}函數(shù)incre在第一次被調(diào)用時,會創(chuàng)建局部變量x并賦初值為20,然后對其進(jìn)行加5的操作并將結(jié)果25返回到主函數(shù)中輸出(注意:隨著函數(shù)執(zhí)行完成后控制流程的返回,函數(shù)中定義的局部變量(自動變量)x被系統(tǒng)自動撤銷)。當(dāng)函數(shù)incre第二次被調(diào)用時,會重新創(chuàng)建局部變量x并賦初值為20,然后對其進(jìn)行加5的操作并將結(jié)果25返回到主函數(shù)中輸出,所以程序執(zhí)行的結(jié)果為:x=25x=25⑴函數(shù)的每次調(diào)用都是使用不同局部變量組。程序演示4.3.1變量的作用域⑵局部變量即使名字相同,互相也無關(guān)系。例4.20編制程序輸出如下所示的字符圖形(每行15個星號,共輸出5行)。***************************************************************************圖4.15字符圖形/*Name:ex04-20.cpp*/#include<stdio.h>voidmain(){ voidmyprint(); inti; for(i=0;i<5;i++) myprint();}voidmyprint(){ inti; for(i=0;i<15;i++) putchar('*'); printf("\n");}程序演示4.3.1變量的作用域局部變量i的作用范圍局部變量i的作用范圍局部變量只在本函數(shù)或本復(fù)合語句內(nèi)才能使用,main()函數(shù)也不例外,主函數(shù)main定義了變量a,b,只在主函數(shù)有效,c在復(fù)合語句內(nèi)有效,在主函數(shù)其他地方無效?!?main(){floata,b;
……{floatc;c=a*b;
……}}4.3.1變量的作用域局部變量C的作用范圍局部變量a、b的作用范圍3.同名全局變量與局部變量作用域重疊問題在某些特定的情況下,可能會出現(xiàn)全局變量和局部變量同名,C規(guī)定按“定義就近原則”來使用的變量。⑴在函數(shù)中如果定義有與全局變量同名的局部變量,則當(dāng)程序的控制流程進(jìn)入到函數(shù)的作用范圍時,程序使用在函數(shù)內(nèi)部(包括形式參數(shù)表和函數(shù)體)定義的局部同名變量。⑵在程序的一個更小局部范圍(復(fù)合語句)中如果定義有與較大范圍(函數(shù)局部或全局)變量同名的變量,則當(dāng)程序的控制流程進(jìn)入到這個小的(復(fù)合語句)局部范圍時,使用在該小局部范圍內(nèi)所定義的局部同名變量;4.3.1變量的作用域intx;voidmain(){ x++; …}voidf1(){intx=1;{intx=2;x++;}x++;}voidf2(){x++;…}全局變量x的作用域圖4.16全局變量與局部變量作用域重疊示意圖復(fù)合語句內(nèi)部定義的局部變量x的作用域①函數(shù)內(nèi)部定義的局部變量x的作用域②③4.3.1變量的作用域例3-26程序的執(zhí)行結(jié)果為(注意輸出順序): 復(fù)合語句中:x=20主函數(shù)中:x=10函數(shù)f1中:x=0 /*全局變量x沒有顯式初始化,默認(rèn)的初始化值為0*/程序演示4.3.1變量的作用域
變量的生存期即是指變量存在的時間。從變量的生存期來看,變量可以分為靜態(tài)存儲變量和動態(tài)存儲變量。數(shù)據(jù)的這種存儲方法稱之為變量的存儲類型。靜態(tài)存儲變量:程序運行時分配固定存儲空間的變量;動態(tài)存儲變量:在程序運行期間,根據(jù)需要進(jìn)行動態(tài)的分配存儲空間的變量。動態(tài)存儲變量能夠提高內(nèi)存的使用效率。4.3.2變量的生存期用戶區(qū)程序區(qū)靜態(tài)存儲區(qū)動態(tài)存儲區(qū)變量的存儲區(qū)存放全局變量、局部變量函數(shù)形參變量、局部變量等一個完整的變量定義格
溫馨提示
- 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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- xxx汽車電腦板項目可行性報告
- 年產(chǎn)xx減肥產(chǎn)品投資項目可行性研究報告
- 新建酸味劑項目立項申請報告
- 新建鱷魚夾項目可行性研究報告
- 2024-2030年新版中國帽釘項目可行性研究報告
- 幼兒語言對話課程設(shè)計
- 2024-2030年撰寫:中國科泰復(fù)行業(yè)發(fā)展趨勢及競爭調(diào)研分析報告
- 2024-2030年撰寫:中國法莫替丁膠囊項目風(fēng)險評估報告
- 2024-2030年撰寫:中國教學(xué)專用投影機(jī)行業(yè)發(fā)展趨勢及競爭調(diào)研分析報告
- 2024-2030年撰寫:中國雙筒過濾器行業(yè)發(fā)展趨勢及競爭調(diào)研分析報告
- 西方文官制度和我國公務(wù)員制度的比較
- 醫(yī)保檢查自查自糾報告
- VBOXTools軟件操作手冊
- 外研版(三年級起點)五年級上冊重點知識點復(fù)習(xí)
- 2023年報告文學(xué)研究(自考)(重點)題庫(帶答案)
- 國軍淞滬會戰(zhàn)
- 2023年湖南體育職業(yè)學(xué)院高職單招(語文)試題庫含答案解析
- GB/T 39314-2020鋁合金石膏型鑄造通用技術(shù)導(dǎo)則
- GB/T 17252-1998聲學(xué)100kHz以下超聲壓電換能器的特性和測量
- GB 16847-1997保護(hù)用電流互感器暫態(tài)特性技術(shù)要求
- 裝飾裝修施工質(zhì)量檢查評分表
評論
0/150
提交評論