版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
c語(yǔ)言程序設(shè)計(jì)現(xiàn)代方法Copyright?2008W.W.Norton&Company.Allrightsreserved.1
本章要點(diǎn)函數(shù)的定義函數(shù)的調(diào)用函數(shù)的聲明形式參數(shù)vs實(shí)際參數(shù)遞歸概述函數(shù)簡(jiǎn)單來(lái)說就是一連串組合在一起并且命名的語(yǔ)句。每個(gè)函數(shù)本質(zhì)上是一個(gè)自帶聲明和語(yǔ)句的小程序。函數(shù)的優(yōu)點(diǎn):可以利用函數(shù)把程序劃分成小塊,這樣便于人們理解和修改程序??梢员苊庵貜?fù)編寫可多次使用的代碼。一個(gè)函數(shù)最初可能是某個(gè)程序的一部分,但可以將其用于其他程序中。3函數(shù)的定義和調(diào)用在介紹定義函數(shù)的規(guī)則之前,先來(lái)看3個(gè)簡(jiǎn)單的定義函數(shù)的程序。4程序:計(jì)算平均值一個(gè)叫做average的函數(shù)用來(lái)計(jì)算兩個(gè)double類型數(shù)值的平均值: doubleaverage(doublea,doubleb) { return(a+b)/2; }在函數(shù)開始處放置的單詞double表示了average函數(shù)的返回類型(returntype)。標(biāo)識(shí)符a和標(biāo)識(shí)符b(即函數(shù)的形式參數(shù)(parameter))表示在調(diào)用average函數(shù)時(shí)提供的求平均值的兩個(gè)數(shù)。5程序:計(jì)算平均值每個(gè)函數(shù)都有一個(gè)用大括號(hào)括起來(lái)的執(zhí)行部分,稱為函數(shù)體(body)。average函數(shù)的函數(shù)體由一條return語(yǔ)句構(gòu)成。執(zhí)行這條語(yǔ)句將會(huì)使函數(shù)“返回”到調(diào)用它的地方,表達(dá)式(a+b)/2的值將作為函數(shù)的返回值。6程序:計(jì)算平均值一個(gè)函數(shù)調(diào)用包括函數(shù)名和其后的實(shí)際參數(shù)(arguments)列表。average(x,y)即為對(duì)average函數(shù)的調(diào)用。實(shí)際參數(shù)用來(lái)給函數(shù)提供信息。調(diào)用average(x,y)的效果就是把變量x和y的值復(fù)制給形式參數(shù)a和b。實(shí)際參數(shù)不一定要是變量;任何正確類型的表達(dá)式都可以。average(5.1,8.9)和average(x/2,y/3)都是合法的。7程序:計(jì)算平均值我們把a(bǔ)verage函數(shù)的調(diào)用放在需要使用其返回值的地方。打印x和y平均值的語(yǔ)句為: printf("Average:%g\n",average(x,y));average的返回值沒有保存;程序顯示出這個(gè)值后就把它丟棄了。如果需要在稍后的程序中用到返回值,可以把這個(gè)返回值賦值給變量: avg=average(x,y);
8程序:計(jì)算平均值程序average.c讀取了3個(gè)數(shù)并且計(jì)算它們的平均值,其中,一次計(jì)算一對(duì)數(shù)的平均值: Enterthreenumbers:3.59.610.2 Averageof3.5and9.6:6.55 Averageof9.6and10.2:9.9 Averageof3.5and10.2:6.859average.c
/*Computespairwiseaveragesofthreenumbers*/
#include<stdio.h>
doubleaverage(doublea,doubleb){return(a+b)/2;}
intmain(void){doublex,y,z;
printf("Enterthreenumbers:");scanf("%lf%lf%lf",&x,&y,&z);printf("Averageof%gand%g:%g\n",x,y,average(x,y));printf("Averageof%gand%g:%g\n",y,z,average(y,z));printf("Averageof%gand%g:%g\n",x,z,average(x,z));
return0;}10程序:顯示倒數(shù)計(jì)數(shù)為了指示出不帶返回值的函數(shù),需要指明這類函數(shù)的返回類型為void: voidprint_count(intn) { printf("Tminus%dandcounting\n",n); }void是一種沒有值的類型。print_count函數(shù)的調(diào)用必須自成一個(gè)語(yǔ)句: print_count(i);程序countdown.c在循環(huán)內(nèi)調(diào)用了print_count函數(shù)10次。11countdown.c
/*Printsacountdown*/
#include<stdio.h>
voidprint_count(intn){printf("Tminus%dandcounting\n",n);}
intmain(void){inti;
for(i=10;i>0;--i)print_count(i);
return0;}12程序:顯示雙關(guān)語(yǔ)(改進(jìn)版)當(dāng)函數(shù)沒有形式參數(shù)時(shí),則在函數(shù)名后面的圓括號(hào)中填入void: voidprint_pun(void) { printf("To
C,
or
not
to
C:
that
is
the
question.\n"); }為了調(diào)用不帶實(shí)際參數(shù)的函數(shù),需要寫出函數(shù)名并且后面跟上一對(duì)圓括號(hào): print_pun();
即使沒有實(shí)際參數(shù)也必須顯示圓括號(hào)。程序pun2.c測(cè)試了print_pun函數(shù)。13pun2.c
/*Printsabadpun*/
#include<stdio.h>
voidprint_pun(void){printf("ToC,ornottoC:thatisthequestion.\n");}
intmain(void){print_pun();return0;}14函數(shù)定義函數(shù)定義的一般格式:
返回類型函數(shù)名(形式參數(shù)) {
聲明
語(yǔ)句 }15函數(shù)定義函數(shù)的“返回類型”是函數(shù)返回值的類型。下列規(guī)則用來(lái)管理返回類型:函數(shù)無(wú)法返回?cái)?shù)組。指定返回類型是void型說明函數(shù)沒有返回值。在C89中,如果忽略返回類型,那么會(huì)假定函數(shù)返回值的類型是int型。在C99中,忽略返回類型是非法的。16函數(shù)定義一些程序員習(xí)慣把返回類型放在函數(shù)名的上邊: double average(doublea,doubleb) { return(a+b)/2; }如果返回類型很冗長(zhǎng),比如unsigned
long
int類型,那么把返回類型單獨(dú)放在一行是非常有用的。17函數(shù)定義函數(shù)名后邊有一串形式參數(shù)列表。每個(gè)形式參數(shù)需要說明其類型;形式參數(shù)間用逗號(hào)進(jìn)行分隔。如果函數(shù)沒有形式參數(shù),那么在圓括號(hào)內(nèi)應(yīng)該出現(xiàn)void。18函數(shù)定義函數(shù)體可以包含聲明和語(yǔ)句。average函數(shù)的變體: doubleaverage(doublea,doubleb) { doublesum;/*declaration*/
sum=a+b;/*statement*/ returnsum/2;/*statement*/ }19函數(shù)定義函數(shù)體內(nèi)聲明的變量專屬于此函數(shù),其他函數(shù)不能對(duì)這些變量進(jìn)行檢查或修改。在C89中,變量聲明必須出現(xiàn)在語(yǔ)句之前。在C99中,變量聲明和語(yǔ)句可以混在一起,只要變量在第一次使用前進(jìn)行聲明即可。20函數(shù)定義返回類型為void的函數(shù)
(“void
函數(shù)”)的函數(shù)體可以為空: voidprint_pun(void) { }在程序開發(fā)過程中,作為一種臨時(shí)措施,留下空函數(shù)體是有意義的。21函數(shù)調(diào)用函數(shù)調(diào)用由函數(shù)名和跟隨其后的實(shí)際參數(shù)列表組成,其中實(shí)際參數(shù)列表用圓括號(hào)括起來(lái): average(x,y) print_count(i) print_pun()如果丟失圓括號(hào),那么將無(wú)法進(jìn)行函數(shù)調(diào)用: print_pun;/***WRONG***/
這條語(yǔ)句是合法的,但是不起作用。22函數(shù)調(diào)用void型的函數(shù)調(diào)用是語(yǔ)句,所以調(diào)用后邊始終跟著分號(hào): print_count(i); print_pun();非void型的函數(shù)調(diào)用產(chǎn)生的值可存儲(chǔ)在變量中,還可以進(jìn)行測(cè)試、顯示或者其他用途: avg=average(x,y); if(average(x,y)>0) printf("Averageispositive\n"); printf("Theaverageis%g\n",
average(x,
y));23函數(shù)調(diào)用如果不需要,非void型函數(shù)的返回值總是可以丟棄: average(x,y);/*discardsreturnvalue*/
這個(gè)調(diào)用是一個(gè)表達(dá)式語(yǔ)句的示例:一個(gè)計(jì)算出了表達(dá)式值但是將其丟棄的語(yǔ)句。24函數(shù)調(diào)用丟棄average的返回值是一件很奇怪的事,但在某些情況下是有意義的:例如:printf返回顯示的字符的個(gè)數(shù)。在下面的調(diào)用后,num_chars的值為9: num_chars=printf("Hi,Mom!\n");通常會(huì)丟掉printf’s的返回值: printf("Hi,Mom!\n"); /*discardsreturnvalue*/25函數(shù)調(diào)用為了清楚地表示故意丟掉函數(shù)返回值,C語(yǔ)言允許在函數(shù)調(diào)用前加上(void): (void)printf("Hi,Mom!\n");使用(void)可以使別人清楚編寫者是故意扔掉返回值的,而不是忘記了。26程序:判定素?cái)?shù)程序prime.c檢查一個(gè)數(shù)是否是素?cái)?shù): Enteranumber:34 Notprime程序使用名為is_prime的函數(shù)來(lái)進(jìn)行檢查。該函數(shù)返回值為true就表示它的形式參數(shù)是素?cái)?shù),返回false就表示它的形式參數(shù)不是素?cái)?shù)。給定數(shù)n后,is_prime函數(shù)把n除以從2到n的平方根之間的每一個(gè)數(shù):如果余數(shù)永遠(yuǎn)為0,就知道n不是素?cái)?shù)。27prime.c
/*Testswhetheranumberisprime*/
#include<stdbool.h>/*C99only*/#include<stdio.h>
boolis_prime(intn){intdivisor;
if(n<=1)returnfalse;for(divisor=2;divisor*divisor<=n;divisor++)if(n%divisor==0)returnfalse;returntrue;}28intmain(void){intn;
printf("Enteranumber:");scanf("%d",&n);if(is_prime(n))printf("Prime\n");elseprintf("Notprime\n");return0;}
29函數(shù)聲明C語(yǔ)言沒有要求函數(shù)的定義必須放置在調(diào)用它之前。假設(shè)重新編排程序average.c,使average函數(shù)的定義放置在main函數(shù)的定義之后:30函數(shù)聲明#include<stdio.h>
intmain(void){doublex,y,z;
printf("Enterthreenumbers:");scanf("%lf%lf%lf",&x,&y,&z);printf("Averageof%gand%g:%g\n",x,y,average(x,y));printf("Averageof%gand%g:%g\n",y,z,average(y,z));printf("Averageof%gand%g:%g\n",x,z,average(x,z));
return0;}
doubleaverage(doublea,doubleb){return(a+b)/2;}31函數(shù)聲明當(dāng)遇到main函數(shù)中第一個(gè)average函數(shù)調(diào)用時(shí),編譯器沒有任何關(guān)于average函數(shù)的信息。但是,編譯器沒有產(chǎn)生錯(cuò)誤信息,而是假設(shè)average函數(shù)返回int型的值。我們將其稱為編譯器為該函數(shù)創(chuàng)建了一個(gè)隱式聲明(implicitdeclaration)。32函數(shù)聲明編譯器無(wú)法檢查傳遞給average的實(shí)參個(gè)數(shù)和實(shí)參類型是否正確。它只能進(jìn)行默認(rèn)的實(shí)際參數(shù)提升并期待最好情況的發(fā)生。當(dāng)編譯器在后面遇到了average的定義時(shí),它發(fā)現(xiàn)該函數(shù)返回值實(shí)際是double而非int,結(jié)果我們將得到一條錯(cuò)誤消息的提示。33函數(shù)聲明為了避免定義前調(diào)用這類問題的發(fā)生,一種方法是安排程序,使每個(gè)函數(shù)的定義都在此函數(shù)調(diào)用之前進(jìn)行??上У氖牵@類安排不總是存在的。而且即使真的做了這類安排,也會(huì)因?yàn)榘凑詹蛔匀坏捻樞蚍胖煤瘮?shù)定義,使程序難以閱讀。34函數(shù)聲明幸運(yùn)的是,C語(yǔ)言提供了一種更好的解決辦法:在調(diào)用前聲明(declare)每個(gè)函數(shù)。函數(shù)聲明(functiondeclaration)使得編譯器對(duì)函數(shù)進(jìn)行概要瀏覽,而函數(shù)的完整定義稍后再出現(xiàn)。函數(shù)聲明的一般形式:
返回類型函數(shù)名(形式參數(shù));函數(shù)的聲明必須與函數(shù)的定義一致。下面是為average函數(shù)添加了聲明后程序的樣子:35函數(shù)聲明#include<stdio.h>
doubleaverage(doublea,doubleb);/*DECLARATION*/
intmain(void){doublex,y,z;
printf("Enterthreenumbers:");scanf("%lf%lf%lf",&x,&y,&z);printf("Averageof%gand%g:%g\n",x,y,average(x,y));printf("Averageof%gand%g:%g\n",y,z,average(y,z));printf("Averageof%gand%g:%g\n",x,z,average(x,z));
return0;}
doubleaverage(doublea,doubleb)/*DEFINITION*/{return(a+b)/2;}36函數(shù)聲明我們把正在討論的這類函數(shù)聲明稱為函數(shù)原型(functionprototype)。C語(yǔ)言還有一種老式的函數(shù)聲明風(fēng)格,即圓括號(hào)內(nèi)可以留空。函數(shù)原型不需要說明函數(shù)形式參數(shù)的名字,只要顯示它們的類型就可以了: doubleaverage(double,double);通常最好是不要忽略形式參數(shù)的名字。37函數(shù)聲明C99遵循這樣的規(guī)則:在調(diào)用一個(gè)函數(shù)之前,必須先對(duì)其進(jìn)行聲明或定義。調(diào)用函數(shù)時(shí),如果此前編譯器未見到該函數(shù)的聲明或定義,會(huì)導(dǎo)致出錯(cuò)。38實(shí)際參數(shù)在C語(yǔ)言中,實(shí)際參數(shù)是通過值傳遞的:調(diào)用函數(shù)時(shí),計(jì)算出每個(gè)實(shí)際參數(shù)的值并且把它賦值給相應(yīng)的形式參數(shù)。在函數(shù)執(zhí)行過程中,對(duì)形式參數(shù)的改變不會(huì)影響實(shí)際參數(shù)的值,這是因?yàn)樾问絽?shù)中包含的是實(shí)際參數(shù)值的副本。39實(shí)際參數(shù)實(shí)際參數(shù)按值傳遞既有利也有弊。既然形式參數(shù)的修改不會(huì)影響到相應(yīng)的實(shí)際參數(shù),那么可以把形式參數(shù)作為函數(shù)內(nèi)的變量來(lái)使用,因此可以減少真正需要的變量的數(shù)量。40實(shí)際參數(shù)思考下面這個(gè)函數(shù),此函數(shù)用來(lái)計(jì)算數(shù)x的n次冪: intpower(intx,intn) { inti,result=1;
for(i=1;i<=n;i++) result=result*x;
returnresult; }41實(shí)際參數(shù)因?yàn)閚只是原始指數(shù)的副本,所以可以在函數(shù)體內(nèi)修改它,因此就不需要使用變量i了: intpower(intx,intn) { intresult=1;
while(n-->0) result=result*x;
returnresult; }42實(shí)際參數(shù)C語(yǔ)言關(guān)于實(shí)際參數(shù)按值傳遞的要求使它很難編寫某些類型的函數(shù)。假設(shè)我們需要一個(gè)函數(shù),它將把double型的值分解成整數(shù)部分和小數(shù)部分。因?yàn)楹瘮?shù)無(wú)法返回兩個(gè)數(shù),所以可以嘗試把兩個(gè)變量傳遞給函數(shù)并且修改它們: voiddecompose(doublex,longint_part, doublefrac_part) { int_part=(long)x; frac_part=x-int_part; }43實(shí)際參數(shù)假設(shè)采用下面的方法調(diào)用這個(gè)函數(shù): decompose(3.14159,i,d);可惜的是,變量i和d不會(huì)因?yàn)橘x值給int_part和frac_part而受到影響。第11章中將闡述如何使decompose函數(shù)正確地工作。44實(shí)際參數(shù)的轉(zhuǎn)換C語(yǔ)言允許在實(shí)際參數(shù)的類型與形式參數(shù)的類型不匹配的情況下進(jìn)行函數(shù)調(diào)用。管理如何轉(zhuǎn)換實(shí)際參數(shù)的規(guī)則與編譯器是否在調(diào)用前遇到函數(shù)(或者函數(shù)的完整定義)的原型有關(guān):45實(shí)際參數(shù)的轉(zhuǎn)換編譯器在調(diào)用前遇到原型。就像使用賦值一樣,每個(gè)實(shí)際參數(shù)的值被隱式地轉(zhuǎn)換成相應(yīng)形式參數(shù)的類型。例如:如果把int類型的實(shí)際參數(shù)傳遞給期望得到double型數(shù)據(jù)的函數(shù),那么會(huì)自動(dòng)把實(shí)際參數(shù)轉(zhuǎn)換成double類型。46實(shí)際參數(shù)的轉(zhuǎn)換編譯器在調(diào)用前沒有遇到原型。編譯器執(zhí)行默認(rèn)的實(shí)際參數(shù)提升:把float型的實(shí)際參數(shù)轉(zhuǎn)換成double類型。執(zhí)行整數(shù)的提升,即把char型和short型的實(shí)際參數(shù)轉(zhuǎn)換成int型(在C99中實(shí)現(xiàn)了整數(shù)提升)。47實(shí)際參數(shù)的轉(zhuǎn)換依賴默認(rèn)的實(shí)際參數(shù)提升是危險(xiǎn)的。例如: #include<stdio.h>
intmain(void) { doublex=3.0; printf("Square:%d\n",square(x));
return0; }
intsquare(intn) { returnn*n; }在調(diào)用square函數(shù)時(shí),編譯器沒有遇到原型,所以不知道該函數(shù)期望有int類型的實(shí)際參數(shù)。48實(shí)際參數(shù)的轉(zhuǎn)換取而代之的,編譯器在變量x上執(zhí)行了沒有效果的默認(rèn)的實(shí)際參數(shù)提升。因?yàn)閟quare函數(shù)期望有int類型的實(shí)際參數(shù),但是卻獲得了double類型值,所以square函數(shù)將產(chǎn)生無(wú)效的結(jié)果。把square’s的實(shí)際參數(shù)強(qiáng)制轉(zhuǎn)換為正確的類型可解決這個(gè)問題: printf("Square:%d\n",square((int)x));當(dāng)然更好的解決方案是在調(diào)用square函數(shù)前提供其原型。在C99中,調(diào)用square之前不提供聲明或者定義是錯(cuò)誤的。49數(shù)組型實(shí)際參數(shù)當(dāng)形式參數(shù)是一維數(shù)組時(shí),可以不說明數(shù)組的長(zhǎng)度: intf(inta[])/*nolengthspecified*/ { … }C語(yǔ)言沒有為函數(shù)提供任何簡(jiǎn)便的方法來(lái)確定傳遞給它的數(shù)組的長(zhǎng)度。但是,如果函數(shù)需要,必須把長(zhǎng)度作為額外的實(shí)際參數(shù)提供出來(lái)。50數(shù)組型實(shí)際參數(shù)例子: intsum_array(inta[],intn) { inti,sum=0;
for(i=0;i<n;i++) sum+=a[i];
returnsum; }因?yàn)閟um_array需要知道a的長(zhǎng)度,所以我們需要將其作為第二個(gè)參數(shù)提供出來(lái)。51數(shù)組型實(shí)際參數(shù)sum_array函數(shù)的原型形式如下: intsum_array(inta[],intn);通常情況下,如果愿意可以忽略形式參數(shù)的名字: intsum_array(int[],int);52數(shù)組型實(shí)際參數(shù)在調(diào)用sum_array函數(shù)時(shí),第一個(gè)參數(shù)是數(shù)組的名字,而第二個(gè)參數(shù)是這個(gè)數(shù)組的長(zhǎng)度。 #defineLEN100
intmain(void) { intb[LEN],total; … total=sum_array(b,LEN); … }注意,在把數(shù)組名傳遞給函數(shù)時(shí),不要在數(shù)組名的后邊放置方括號(hào): total=sum_array(b[],LEN);/***WRONG***/53數(shù)組型實(shí)際參數(shù)函數(shù)無(wú)法檢測(cè)通過傳遞是否獲得了正確的數(shù)組長(zhǎng)度。人們可以利用這個(gè)事實(shí),方法是告訴函數(shù)數(shù)組比實(shí)際小得多。假設(shè)雖然數(shù)組b可以擁有100個(gè)元素,但是實(shí)際僅存儲(chǔ)了50個(gè)元素。通過書寫下列語(yǔ)句可以對(duì)數(shù)組的前50個(gè)元素進(jìn)行求和: total=sum_array(b,50);54數(shù)組型實(shí)際參數(shù)注意不要告訴函數(shù),數(shù)組型實(shí)際參數(shù)要比實(shí)際的大: total
=
sum_array(b,
150);
/***
WRONG
***/
sum_array函數(shù)將超出數(shù)組的末尾,導(dǎo)致不可知的行為。55數(shù)組型實(shí)際參數(shù)函數(shù)可以改變數(shù)組型形式參數(shù)的元素,且改變會(huì)在相應(yīng)的實(shí)際參數(shù)中體現(xiàn)出來(lái)。下面的函數(shù)通過在每個(gè)數(shù)組元素中存儲(chǔ)0來(lái)修改數(shù)組: voidstore_zeros(inta[],intn) { inti;
for(i=0;i<n;i++) a[i]=0; }56數(shù)組型實(shí)際參數(shù)調(diào)用store_zeros: store_zeros(b,100);數(shù)組型實(shí)際參數(shù)的元素可以修改似乎與C語(yǔ)言中實(shí)際參數(shù)的值傳遞相矛盾。第12章中將解釋這其實(shí)不矛盾。57數(shù)組型實(shí)際參數(shù)如果形式參數(shù)是多維數(shù)組,則只有第一維的長(zhǎng)度可以省略。如果我們修改sum_array函數(shù),使得a是個(gè)二維數(shù)組,則必須指定a中列的數(shù)量: #defineLEN10
int
sum_two_dimensional_array(int
a[][LEN],
int
n) { inti,j,sum=0;
for(i=0;i<n;i++) for(j=0;j<LEN;j++) sum+=a[i][j];
returnsum; }58數(shù)組型實(shí)際參數(shù)不能傳遞具有任意列數(shù)的多維數(shù)組是很討厭的。但我們經(jīng)??梢酝ㄟ^使用指針數(shù)組來(lái)解決這個(gè)問題。C99中的變長(zhǎng)數(shù)組形式參數(shù)為上述問題提供了一個(gè)更好的解決方案。59變長(zhǎng)型數(shù)組形式參數(shù)(C99)C99允許使用變長(zhǎng)型數(shù)組作為形式參數(shù)。考慮sum_array函數(shù): intsum_array(inta[],intn) { … }
這樣的定義使得n和數(shù)組a的長(zhǎng)度之間沒有直接的聯(lián)系。盡管函數(shù)體會(huì)將n看作數(shù)組a的長(zhǎng)度,但是數(shù)組的實(shí)際長(zhǎng)度有可能比n大或者小。60變長(zhǎng)型數(shù)組形式參數(shù)(C99)如果使用變長(zhǎng)數(shù)組形式參數(shù),我們可以明確說明數(shù)組a的長(zhǎng)度就是n: intsum_array(intn,inta[n]) { … }第一個(gè)參數(shù)(n)的值確定了第二個(gè)參數(shù)(a)的長(zhǎng)度。注意這里交換了形式參數(shù)的順序,使用變長(zhǎng)數(shù)組形式參數(shù)時(shí),參數(shù)的順序很重要。61變長(zhǎng)型數(shù)組形式參數(shù)(C99)對(duì)于新版本的sum_array函數(shù),其函數(shù)原型有好幾種寫法:一種寫法是使其看起來(lái)跟函數(shù)定義一樣: int
sum_array(int
n,
int
a[n]);
/*
Version1
*/另一種寫法是用*(星號(hào))取代數(shù)組長(zhǎng)度: int
sum_array(int
n,
int
a[*]);
/*
Version2a
*/62變長(zhǎng)型數(shù)組形式參數(shù)(C99)使用*的理由是:函數(shù)聲明時(shí),形式參數(shù)的名字是可選的。如果第一個(gè)參數(shù)定義被省略了,那么就沒有辦法說明數(shù)組的長(zhǎng)度是n,而*的使用則為我們提供了一個(gè)線索——數(shù)組的長(zhǎng)度與形式參數(shù)列表中前面的參數(shù)相關(guān): int
sum_array(int,
int
[*]);
/*
Version
2b
*/63變長(zhǎng)型數(shù)組形式參數(shù)(C99)另外,方括號(hào)中為空也是合法的。在聲明數(shù)組參數(shù)中我們經(jīng)常這么做: intsum_array(intn,inta[]);/*Version3a*/ intsum_array(int,int[]);/*Version3b*/讓括號(hào)為空不是一個(gè)好的選擇,因?yàn)檫@樣并沒有說明n和a的關(guān)系。64變長(zhǎng)型數(shù)組形式參數(shù)(C99)一般來(lái)說,變長(zhǎng)數(shù)組形式參數(shù)的長(zhǎng)度可以是任意表達(dá)式。假設(shè)要編寫一個(gè)函數(shù)來(lái)連接數(shù)組a和b,結(jié)果存到第三個(gè)數(shù)組c中: intconcatenate(int
m,
int
n,
int
a[m],
int
b[n], int
c[m+n]) { … }這里用于指定數(shù)組C長(zhǎng)度的表達(dá)式只用到了另外兩個(gè)參數(shù);但一般來(lái)說,該表達(dá)式可以使用函數(shù)外部的變量,甚至可以調(diào)用其他函數(shù)。65變長(zhǎng)型數(shù)組形式參數(shù)(C99)一維變長(zhǎng)數(shù)組做形式參數(shù)的用途往往是有限的。一維變長(zhǎng)數(shù)組形式參數(shù)通過指定數(shù)組參數(shù)的長(zhǎng)度使得函數(shù)的聲明和定義更具描述性。但是,由于沒有進(jìn)行額外的錯(cuò)誤檢測(cè),數(shù)組參數(shù)仍然有可能太長(zhǎng)或太短。66變長(zhǎng)型數(shù)組形式參數(shù)(C99)如果變長(zhǎng)數(shù)組參數(shù)是多維的則更加實(shí)用。如果使用變長(zhǎng)數(shù)組形式參數(shù),則可以把sum_two_dimensional_array函數(shù)推廣到任意列數(shù)的情況: int
sum_two_dimensional_array(int
n,
int
m,
int
a[n][m]) { inti,j,sum=0;
for(i=0;i<n;i++) for(j=0;j<m;j++) sum+=a[i][j];
returnsum; }67變長(zhǎng)型數(shù)組形式參數(shù)(C99)這個(gè)函數(shù)的原型可以是以下幾種: int
sum_two_dimensional_array(int
n,
int
m,
int
a[n][m]); int
sum_two_dimensional_array(int
n,
int
m,
int
a[*][*]); int
sum_two_dimensional_array(int
n,
int
m,
int
a[][m]); int
sum_two_dimensional_array(int
n,
int
m,
int
a[][*]);68在數(shù)組參數(shù)聲明中使用static(C99)C99允許在數(shù)組參數(shù)聲明中使用關(guān)鍵字static。在下面這個(gè)例子中,將static放在數(shù)字3之前表明數(shù)組a的長(zhǎng)度至少可以保證是3: intsum_array(inta[static3],intn) { … }69在數(shù)組參數(shù)聲明中使用static(C99)使用static不會(huì)對(duì)程序的行為有任何影響。static的存在只不過是一個(gè)“提示”,C編譯器可以據(jù)此生成更快的指令來(lái)訪問數(shù)組。如果數(shù)組參數(shù)是多維的,static僅可用于第一維。70復(fù)合字面量(C99)讓我們?cè)賮?lái)看看最初的sum_array函數(shù)。當(dāng)調(diào)用sum_array時(shí),第一個(gè)實(shí)際參數(shù)通常是數(shù)組名。例如: intb[]={3,0,3,4,1}; total=sum_array(b,5);這樣寫的唯一問題是需要把b作為一個(gè)變量聲明,并在調(diào)用前進(jìn)行初始化。如果b不作它用,而是僅為了調(diào)用sum_array來(lái)創(chuàng)建它,則有些浪費(fèi)。71復(fù)合字面量(C99)在C99中,可以使用復(fù)合字面量(compoundliteral)來(lái)避免該問題:復(fù)合字面量是通過指定其包含的元素而創(chuàng)建的沒有名字的數(shù)組。下面調(diào)用sum_array函數(shù),第一個(gè)參數(shù)(黑體)就是一個(gè)復(fù)合字面量: total
=
sum_array((int
[]){3,
0,
3,
4,
1},
5);這里沒有對(duì)數(shù)組的長(zhǎng)度進(jìn)行特別的說明,是由復(fù)合字面量的元素個(gè)數(shù)決定的。當(dāng)然,也可以對(duì)長(zhǎng)度做準(zhǔn)確說明 (int[4]){1,9,2,1}
等同于 (int[]){1,9,2,1}72復(fù)合字面量(C99)復(fù)合字面量類似于應(yīng)用于初始化式的強(qiáng)制轉(zhuǎn)換。事實(shí)上,復(fù)合字面量和初始化式遵守同樣的規(guī)則。復(fù)合字面量可以包含指示符,就像指定初始化式一樣;可以不提供完全的初始化(未初始化的元素默認(rèn)被初始化為零)。例如,復(fù)合字面量(int[10]){8,6}有10個(gè)元素;前兩個(gè)元素的值為8和6,剩下的元素值為0。73復(fù)合字面量(C99)函數(shù)內(nèi)部創(chuàng)建的復(fù)合字面量可以包含任意的表達(dá)式,不限于常量: total
=
sum_array((int
[]){2
*
i,
i
+
j,
j
*
k},
3);復(fù)合字面量為左值,所以其元素的值可以改變。如果要求其值為“只讀”,可以在類型前加上const: (const
int
[]){5,
4}74return語(yǔ)句非void的函數(shù)必須使用return語(yǔ)句來(lái)說明將要返回的值。return語(yǔ)句有如下格式: return表達(dá)式
;表達(dá)式經(jīng)常只是常量或變量: return0; returnstatus;也可能是更加復(fù)雜的表達(dá)式: returnn>=0?n:0;75return語(yǔ)句如果return語(yǔ)句中表達(dá)式的類型和函數(shù)的返回類型不匹配,那么系統(tǒng)將會(huì)把表達(dá)式的類型隱式轉(zhuǎn)換成返回類型。例如,如果聲明函數(shù)返回int型值,但是return語(yǔ)句包含double型表達(dá)式,那么系統(tǒng)將會(huì)把表達(dá)式的值轉(zhuǎn)換成int型。76return語(yǔ)句如果沒有給出表達(dá)式,return語(yǔ)句可以出現(xiàn)在返回類型為void的函數(shù)中: return;/*
return
in
a
void
function
*/例子: voidprint_int(inti) { if(i<0) return; printf("%d",i); }
77return語(yǔ)句Return語(yǔ)句可以出現(xiàn)在void函數(shù)的末尾: voidprint_pun(void) { printf("To
C,
or
not
to
C:
that
is
the
question.\n"); return;/*OK,butnotneeded*/ }
這里return語(yǔ)句不是必需的。如果非void函數(shù)到達(dá)了函數(shù)體的末尾(也就是說沒有執(zhí)行return語(yǔ)句),那么如果程序試圖使用函數(shù)的返回值,其行為將是未定義的。78程序終止正常情況下,main的返回類型是int: intmain(void) { … }以往的C程序常常省略main的返回類型,這其實(shí)是利用了返回類型默認(rèn)為int類型的傳統(tǒng): main() { … }79程序終止省略函數(shù)的返回類型在C99中是非法的,所以應(yīng)避免這么做。省略main函數(shù)參數(shù)列表中的void是合法的,但是最好顯式地表明main函數(shù)沒有參數(shù)。80程序終止main數(shù)返回的值是狀態(tài)碼,在程序終止時(shí)可以檢測(cè)到狀態(tài)碼。如果程序正常終止,main函數(shù)應(yīng)該返回0。為了說明異常終止,main函數(shù)應(yīng)該返回非0的值。確信每個(gè)C程序都返回狀態(tài)碼是一個(gè)很好的實(shí)踐。81exit函數(shù)在main函數(shù)中執(zhí)行return語(yǔ)句是終止程序的一種方法。另一種方法是調(diào)用exit函數(shù),此函數(shù)屬于<stdlib.h>。傳遞給exit函數(shù)的實(shí)際參數(shù)和main函數(shù)的返回值具有相同的含義:兩者都說明程序終止時(shí)的狀態(tài)。為了說明正常終止,傳遞0: exit(0);/*normaltermination*/82exit函數(shù)因?yàn)?有些模糊,所以C語(yǔ)言允許用傳遞EXIT_SUCCESS來(lái)代替(效果是相同的): exit(EXIT_SUCCESS);傳遞EXIT_FAILURE表示異常終止: exit(EXIT_FAILURE);EXIT_SUCCESS和EXIT_FAILURE是定義在<stdlib.h>中的宏。EXIT_SUCCESS和EXIT_FAILURE的值都是事先定義的,通常分別為0和1。83exit函數(shù)Main函數(shù)中的語(yǔ)句 return表達(dá)式;
等價(jià)于 exit(表達(dá)式);return語(yǔ)句和exit函數(shù)之間的差異是:不管哪個(gè)函數(shù)調(diào)用exit函數(shù)都會(huì)導(dǎo)致程序終止。Return語(yǔ)句僅當(dāng)由main函數(shù)調(diào)用時(shí)才會(huì)導(dǎo)致程序終止。84遞歸如果函數(shù)調(diào)用它本身,那么此函數(shù)就是遞歸的(recursive)。例如,利用公式n!=n×(n–1)!,下面的函數(shù)可以遞歸地計(jì)算出n!的結(jié)果: intfact(intn) { if(n<=1) return1; else returnn*fact(n-1); }85為了了解遞歸的工作原理,一起來(lái)跟蹤下面這個(gè)語(yǔ)句的執(zhí)行: i=fact(3); fact(3)
發(fā)現(xiàn)3不是小于或等于1,所以它調(diào)用
fact(2),此函數(shù)發(fā)現(xiàn)2不是小于或等于1,所以它調(diào)用
fact(1),此函數(shù)發(fā)現(xiàn)1是小于或等于1的,所以它返回1,從而導(dǎo)致
fact(2)
返回2×1=2,從而導(dǎo)致 fact(3)
返回3×2=6。遞歸86遞歸下面的遞歸函數(shù)利用公式xn=x×xn–1計(jì)算出xn的值: intpower(intx,intn) { if(n==0) return1; else returnx*power(x,n-1); }87遞歸通過把條件表達(dá)式放入return語(yǔ)句中,可以精簡(jiǎn)power函數(shù): intpower(intx,intn) { return
n
==
0
?
1
:
x
*
power(x,
n
-
1); }一旦被調(diào)用,fact函數(shù)和power函數(shù)都會(huì)仔細(xì)地測(cè)試“終止條件”。所有遞歸函數(shù)都需要某些類型的終止條件。88快速排序算法遞歸對(duì)要求函數(shù)調(diào)用自身兩次或多次的復(fù)雜算法非常有幫助。實(shí)際上,遞歸經(jīng)常作為分治法(divide-and-conquer)技術(shù)的結(jié)果自然地出現(xiàn)。這種稱為分治法的算法設(shè)計(jì)方法把一個(gè)大問題劃分成多個(gè)較小的問題,然后采用相同的算法分別解決這些小問題。
89分治法的經(jīng)典示例就是流行的排序算法—快速排序(quicksort)。假設(shè)要排序的數(shù)組的下標(biāo)從1到n。
快速排序算法(1)選擇數(shù)組元素e(作為“分割元素”),然后重新排列數(shù)組使得元素從1一直到i-1都是小于或等于元素e的,元素i包含e,而元素從i+1一直到n都是大于或等于e的。(2)通過遞歸地采用快速排序方法,對(duì)從1到i-1的元素進(jìn)行排序。(3)通過遞歸地采用快速排序方法,對(duì)從i+1到n的元素進(jìn)行排序。
快速排序算法90快速排序算法顯然快速排序中的第1步是很關(guān)鍵的。有許多種方法可以用來(lái)分割數(shù)組。我們采用的方法是很容易理解的,但是它不是特別有效。該算法依賴于兩個(gè)名為low和high的標(biāo)記,這兩個(gè)標(biāo)記用來(lái)跟蹤數(shù)組內(nèi)的位置。91快速排序算法開始,low指向數(shù)組中的第一個(gè)元素,而high指向末尾元素。首先把第一個(gè)元素(分割元素)復(fù)制給其他地方的一個(gè)臨時(shí)存儲(chǔ)單元,從而在數(shù)組中留出一個(gè)“空位”。接下來(lái),從右向左移動(dòng)high,直到high指向小于分割元素的數(shù)時(shí)停止。然后把這個(gè)數(shù)復(fù)制給low指向的空位,這將產(chǎn)生一個(gè)新的空位(high指向的)?,F(xiàn)在從左向右移動(dòng)low,尋找大于分割元素的數(shù)。在找到時(shí),把這個(gè)找到的數(shù)復(fù)制給high指向的空位。重復(fù)執(zhí)行此過程,交替操作low和high直到兩者在數(shù)組中間的某處相遇時(shí)停止。此時(shí),兩個(gè)標(biāo)記都將指向空位:只要把分割元素復(fù)制給空位就夠了。92快速排序算法分割數(shù)組的示例:93快速排序算法從最后一個(gè)圖可以看出,分割元素左側(cè)的所有元素都小于或等于12,而其右側(cè)的所有元素都大于或等于12。既然己經(jīng)分割了數(shù)組,那么就可以使用快速排序法對(duì)數(shù)組的前4個(gè)元素(10,3,6,和7)和后2個(gè)元素(15和18)進(jìn)行遞歸快速排序了。94程序:快速排序讓我們先來(lái)開發(fā)一個(gè)名為quicksort的遞歸函數(shù),此函數(shù)采用快速排序算法對(duì)整型數(shù)組進(jìn)行排序。程序qsort.c將10個(gè)數(shù)讀入一個(gè)數(shù)組,調(diào)用quicksort函數(shù)來(lái)對(duì)數(shù)組進(jìn)行排序,然后打印數(shù)組中的元素: Enter
10
numbers
to
be
sorted:
9
16
47
82
4
66
12
3
25
51 In
sorted
order:
3
4
9
12
16
25
47
51
66
82分割數(shù)組的代碼放置在名為split的獨(dú)立的函數(shù)中。95qsort.c
/*SortsanarrayofintegersusingQuicksortalgorithm*/
#include<stdio.h>
#defineN10
voidquicksort(inta[],intlow,inthigh);intsplit(inta[],intlow,inthigh);
intmain(void){inta[N],i;
printf("Enter%dnumberstobesorted:",N);for(i=0;i<N;i++)scanf("%d",&a[i]);quicksort(a,0,N-1);
printf("Insortedorder:");for(i=0;i<N;i++)printf("%d",a[i]);printf("\n");
return0;}96voidquicksort(inta[],intlow,inthigh){intmiddle;
if(low>=high)return;middle=split(a,low,high);quicksort(a,low,middle-1);quicksort(a,middle+1,high);}97intsplit(inta[],intlow,inthigh){intpart_element=a[low];
for(;;){while(low<high&&part_element<=a[high])high--;if(low>=high)break;a[low++]=a[high];
while(low<high&&a[low]<=part_element)low++;if(low>=high)break;a[high--]=a[low];}
a[high]=pa
溫馨提示
- 1. 本站所有資源如無(wú)特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 課題組成員培訓(xùn)
- ??谱o(hù)士培訓(xùn)收獲
- 3.1 水循環(huán)(分層練習(xí))高一地理同步高效課堂(人教版2019必修第一冊(cè))
- T-YNZYC 0083-2023 綠色藥材 云黃連種苗生產(chǎn)技術(shù)規(guī)程
- T-YNAEPI 0001-2024 有機(jī)固廢低溫絕氧碳化處理工程技術(shù)規(guī)范
- 期中模擬試卷(1-4單元)(試題)2024-2025學(xué)年六年級(jí)上冊(cè)數(shù)學(xué)人教版
- 穿越刺繡的時(shí)尚語(yǔ)言-抽紗刺繡與現(xiàn)代時(shí)裝設(shè)計(jì)探索
- Windows Server網(wǎng)絡(luò)管理項(xiàng)目教程(Windows Server 2022)(微課版)9.2 任務(wù)1 安裝VPN服務(wù)器
- 幼兒教育繪本分享-幼兒教育專家
- 山東省滕州市2024-2025學(xué)年上學(xué)期中練習(xí)九年級(jí)英語(yǔ)試題(無(wú)答案)
- 幕墻施工計(jì)劃書
- 鹵味官方直播話術(shù)
- 【湯臣倍健經(jīng)營(yíng)戰(zhàn)略分析9000字(論文)】
- 供應(yīng)鏈方案設(shè)計(jì)
- 國(guó)防教育基地現(xiàn)狀分析報(bào)告
- 高壓氣瓶的安全知識(shí)
- 林業(yè)林權(quán)流轉(zhuǎn)與經(jīng)營(yíng)主體培育
- 二年級(jí)學(xué)生的拖地勞動(dòng)教案
- 新會(huì)陳皮培訓(xùn)課件
- 浙江科天水性科技有限責(zé)任公司年產(chǎn)100000噸水性聚氨酯合成革樹脂項(xiàng)目環(huán)境影響報(bào)告書
- 答題卡填涂注意事項(xiàng)
評(píng)論
0/150
提交評(píng)論