c語言程序設(shè)計現(xiàn)代方法課件_第1頁
c語言程序設(shè)計現(xiàn)代方法課件_第2頁
c語言程序設(shè)計現(xiàn)代方法課件_第3頁
c語言程序設(shè)計現(xiàn)代方法課件_第4頁
c語言程序設(shè)計現(xiàn)代方法課件_第5頁
已閱讀5頁,還剩99頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

c語言程序設(shè)計現(xiàn)代方法Copyright?2008W.W.Norton&Company.Allrightsreserved.1c語言程序設(shè)計現(xiàn)代方法Copyright?2008W.

本章要點函數(shù)的定義函數(shù)的調(diào)用函數(shù)的聲明形式參數(shù)vs實際參數(shù)遞歸本章要點函數(shù)的定義概述函數(shù)簡單來說就是一連串組合在一起并且命名的語句。每個函數(shù)本質(zhì)上是一個自帶聲明和語句的小程序。函數(shù)的優(yōu)點:可以利用函數(shù)把程序劃分成小塊,這樣便于人們理解和修改程序。可以避免重復(fù)編寫可多次使用的代碼。一個函數(shù)最初可能是某個程序的一部分,但可以將其用于其他程序中。3概述函數(shù)簡單來說就是一連串組合在一起并且命名的語句。3函數(shù)的定義和調(diào)用在介紹定義函數(shù)的規(guī)則之前,先來看3個簡單的定義函數(shù)的程序。4函數(shù)的定義和調(diào)用在介紹定義函數(shù)的規(guī)則之前,先來看3個簡單的定程序:計算平均值一個叫做average的函數(shù)用來計算兩個double類型數(shù)值的平均值: doubleaverage(doublea,doubleb) { return(a+b)/2; }在函數(shù)開始處放置的單詞double表示了average函數(shù)的返回類型(returntype)。標(biāo)識符a和標(biāo)識符b(即函數(shù)的形式參數(shù)(parameter))表示在調(diào)用average函數(shù)時提供的求平均值的兩個數(shù)。5程序:計算平均值一個叫做average的函數(shù)用來計算兩個do程序:計算平均值每個函數(shù)都有一個用大括號括起來的執(zhí)行部分,稱為函數(shù)體(body)。average函數(shù)的函數(shù)體由一條return語句構(gòu)成。執(zhí)行這條語句將會使函數(shù)“返回”到調(diào)用它的地方,表達(dá)式(a+b)/2的值將作為函數(shù)的返回值。6程序:計算平均值每個函數(shù)都有一個用大括號括起來的執(zhí)行部分,稱程序:計算平均值一個函數(shù)調(diào)用包括函數(shù)名和其后的實際參數(shù)(arguments)列表。average(x,y)即為對average函數(shù)的調(diào)用。實際參數(shù)用來給函數(shù)提供信息。調(diào)用average(x,y)的效果就是把變量x和y的值復(fù)制給形式參數(shù)a和b。實際參數(shù)不一定要是變量;任何正確類型的表達(dá)式都可以。average(5.1,8.9)和average(x/2,y/3)都是合法的。7程序:計算平均值一個函數(shù)調(diào)用包括函數(shù)名和其后的實際參數(shù)(a程序:計算平均值我們把a(bǔ)verage函數(shù)的調(diào)用放在需要使用其返回值的地方。打印x和y平均值的語句為: printf("Average:%g\n",average(x,y));average的返回值沒有保存;程序顯示出這個值后就把它丟棄了。如果需要在稍后的程序中用到返回值,可以把這個返回值賦值給變量: avg=average(x,y);

8程序:計算平均值我們把a(bǔ)verage函數(shù)的調(diào)用放在需要使用其程序:計算平均值程序average.c讀取了3個數(shù)并且計算它們的平均值,其中,一次計算一對數(shù)的平均值: Enterthreenumbers:3.59.610.2 Averageof3.5and9.6:6.55 Averageof9.6and10.2:9.9 Averageof3.5and10.2:6.859程序:計算平均值程序average.c讀取了3個數(shù)并且計算它average.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;}10average.c10程序:顯示倒數(shù)計數(shù)為了指示出不帶返回值的函數(shù),需要指明這類函數(shù)的返回類型為void: voidprint_count(intn) { printf("Tminus%dandcounting\n",n); }void是一種沒有值的類型。print_count函數(shù)的調(diào)用必須自成一個語句: print_count(i);程序countdown.c在循環(huán)內(nèi)調(diào)用了print_count函數(shù)10次。11程序:顯示倒數(shù)計數(shù)為了指示出不帶返回值的函數(shù),需要指明這類函countdown.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;}12countdown.c12程序:顯示雙關(guān)語(改進(jìn)版)當(dāng)函數(shù)沒有形式參數(shù)時,則在函數(shù)名后面的圓括號中填入void: voidprint_pun(void) { printf("To

C,

or

not

to

C:

that

is

the

question.\n"); }為了調(diào)用不帶實際參數(shù)的函數(shù),需要寫出函數(shù)名并且后面跟上一對圓括號: print_pun();

即使沒有實際參數(shù)也必須顯示圓括號。程序pun2.c測試了print_pun函數(shù)。13程序:顯示雙關(guān)語(改進(jìn)版)當(dāng)函數(shù)沒有形式參數(shù)時,則在函數(shù)名后pun2.c

/*Printsabadpun*/

#include<stdio.h>

voidprint_pun(void){printf("ToC,ornottoC:thatisthequestion.\n");}

intmain(void){print_pun();return0;}14pun2.c14函數(shù)定義函數(shù)定義的一般格式:

返回類型函數(shù)名(形式參數(shù)) {

聲明

語句 }15函數(shù)定義函數(shù)定義的一般格式:15函數(shù)定義函數(shù)的“返回類型”是函數(shù)返回值的類型。下列規(guī)則用來管理返回類型:函數(shù)無法返回數(shù)組。指定返回類型是void型說明函數(shù)沒有返回值。在C89中,如果忽略返回類型,那么會假定函數(shù)返回值的類型是int型。在C99中,忽略返回類型是非法的。16函數(shù)定義函數(shù)的“返回類型”是函數(shù)返回值的類型。16函數(shù)定義一些程序員習(xí)慣把返回類型放在函數(shù)名的上邊: double average(doublea,doubleb) { return(a+b)/2; }如果返回類型很冗長,比如unsigned

long

int類型,那么把返回類型單獨(dú)放在一行是非常有用的。17函數(shù)定義一些程序員習(xí)慣把返回類型放在函數(shù)名的上邊:17函數(shù)定義函數(shù)名后邊有一串形式參數(shù)列表。每個形式參數(shù)需要說明其類型;形式參數(shù)間用逗號進(jìn)行分隔。如果函數(shù)沒有形式參數(shù),那么在圓括號內(nèi)應(yīng)該出現(xiàn)void。18函數(shù)定義函數(shù)名后邊有一串形式參數(shù)列表。18函數(shù)定義函數(shù)體可以包含聲明和語句。average函數(shù)的變體: doubleaverage(doublea,doubleb) { doublesum;/*declaration*/

sum=a+b;/*statement*/ returnsum/2;/*statement*/ }19函數(shù)定義函數(shù)體可以包含聲明和語句。19函數(shù)定義函數(shù)體內(nèi)聲明的變量專屬于此函數(shù),其他函數(shù)不能對這些變量進(jìn)行檢查或修改。在C89中,變量聲明必須出現(xiàn)在語句之前。在C99中,變量聲明和語句可以混在一起,只要變量在第一次使用前進(jìn)行聲明即可。20函數(shù)定義函數(shù)體內(nèi)聲明的變量專屬于此函數(shù),其他函數(shù)不能對這些變函數(shù)定義返回類型為void的函數(shù)

(“void

函數(shù)”)的函數(shù)體可以為空: voidprint_pun(void) { }在程序開發(fā)過程中,作為一種臨時措施,留下空函數(shù)體是有意義的。21函數(shù)定義返回類型為void的函數(shù)(“void函數(shù)”)函數(shù)調(diào)用函數(shù)調(diào)用由函數(shù)名和跟隨其后的實際參數(shù)列表組成,其中實際參數(shù)列表用圓括號括起來: average(x,y) print_count(i) print_pun()如果丟失圓括號,那么將無法進(jìn)行函數(shù)調(diào)用: print_pun;/***WRONG***/

這條語句是合法的,但是不起作用。22函數(shù)調(diào)用函數(shù)調(diào)用由函數(shù)名和跟隨其后的實際參數(shù)列表組成,其中實函數(shù)調(diào)用void型的函數(shù)調(diào)用是語句,所以調(diào)用后邊始終跟著分號: print_count(i); print_pun();非void型的函數(shù)調(diào)用產(chǎn)生的值可存儲在變量中,還可以進(jìn)行測試、顯示或者其他用途: 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ù)調(diào)用是語句,所以調(diào)用后邊始終跟著分號函數(shù)調(diào)用如果不需要,非void型函數(shù)的返回值總是可以丟棄: average(x,y);/*discardsreturnvalue*/

這個調(diào)用是一個表達(dá)式語句的示例:一個計算出了表達(dá)式值但是將其丟棄的語句。24函數(shù)調(diào)用如果不需要,非void型函數(shù)的返回值總是可以丟棄:2函數(shù)調(diào)用丟棄average的返回值是一件很奇怪的事,但在某些情況下是有意義的:例如:printf返回顯示的字符的個數(shù)。在下面的調(diào)用后,num_chars的值為9: num_chars=printf("Hi,Mom!\n");通常會丟掉printf’s的返回值: printf("Hi,Mom!\n"); /*discardsreturnvalue*/25函數(shù)調(diào)用丟棄average的返回值是一件很奇怪的事,但在某些函數(shù)調(diào)用為了清楚地表示故意丟掉函數(shù)返回值,C語言允許在函數(shù)調(diào)用前加上(void): (void)printf("Hi,Mom!\n");使用(void)可以使別人清楚編寫者是故意扔掉返回值的,而不是忘記了。26函數(shù)調(diào)用為了清楚地表示故意丟掉函數(shù)返回值,C語言允許在函數(shù)調(diào)程序:判定素數(shù)程序prime.c檢查一個數(shù)是否是素數(shù): Enteranumber:34 Notprime程序使用名為is_prime的函數(shù)來進(jìn)行檢查。該函數(shù)返回值為true就表示它的形式參數(shù)是素數(shù),返回false就表示它的形式參數(shù)不是素數(shù)。給定數(shù)n后,is_prime函數(shù)把n除以從2到n的平方根之間的每一個數(shù):如果余數(shù)永遠(yuǎn)為0,就知道n不是素數(shù)。27程序:判定素數(shù)程序prime.c檢查一個數(shù)是否是素數(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;}28prime.c28intmain(void){intn;

printf("Enteranumber:");scanf("%d",&n);if(is_prime(n))printf("Prime\n");elseprintf("Notprime\n");return0;}

29intmain(void)29函數(shù)聲明C語言沒有要求函數(shù)的定義必須放置在調(diào)用它之前。假設(shè)重新編排程序average.c,使average函數(shù)的定義放置在main函數(shù)的定義之后:30函數(shù)聲明C語言沒有要求函數(shù)的定義必須放置在調(diào)用它之前。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ù)聲明#include<stdio.h>31函數(shù)聲明當(dāng)遇到main函數(shù)中第一個average函數(shù)調(diào)用時,編譯器沒有任何關(guān)于average函數(shù)的信息。但是,編譯器沒有產(chǎn)生錯誤信息,而是假設(shè)average函數(shù)返回int型的值。我們將其稱為編譯器為該函數(shù)創(chuàng)建了一個隱式聲明(implicitdeclaration)。32函數(shù)聲明當(dāng)遇到main函數(shù)中第一個average函數(shù)調(diào)用時,函數(shù)聲明編譯器無法檢查傳遞給average的實參個數(shù)和實參類型是否正確。它只能進(jìn)行默認(rèn)的實際參數(shù)提升并期待最好情況的發(fā)生。當(dāng)編譯器在后面遇到了average的定義時,它發(fā)現(xiàn)該函數(shù)返回值實際是double而非int,結(jié)果我們將得到一條錯誤消息的提示。33函數(shù)聲明編譯器無法檢查傳遞給average的實參個數(shù)和實參類函數(shù)聲明為了避免定義前調(diào)用這類問題的發(fā)生,一種方法是安排程序,使每個函數(shù)的定義都在此函數(shù)調(diào)用之前進(jìn)行??上У氖牵@類安排不總是存在的。而且即使真的做了這類安排,也會因為按照不自然的順序放置函數(shù)定義,使程序難以閱讀。34函數(shù)聲明為了避免定義前調(diào)用這類問題的發(fā)生,一種方法是安排程序函數(shù)聲明幸運(yùn)的是,C語言提供了一種更好的解決辦法:在調(diào)用前聲明(declare)每個函數(shù)。函數(shù)聲明(functiondeclaration)使得編譯器對函數(shù)進(jìn)行概要瀏覽,而函數(shù)的完整定義稍后再出現(xiàn)。函數(shù)聲明的一般形式:

返回類型函數(shù)名(形式參數(shù));函數(shù)的聲明必須與函數(shù)的定義一致。下面是為average函數(shù)添加了聲明后程序的樣子:35函數(shù)聲明幸運(yùn)的是,C語言提供了一種更好的解決辦法:在調(diào)用前聲函數(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ù)聲明#include<stdio.h>36函數(shù)聲明我們把正在討論的這類函數(shù)聲明稱為函數(shù)原型(functionprototype)。C語言還有一種老式的函數(shù)聲明風(fēng)格,即圓括號內(nèi)可以留空。函數(shù)原型不需要說明函數(shù)形式參數(shù)的名字,只要顯示它們的類型就可以了: doubleaverage(double,double);通常最好是不要忽略形式參數(shù)的名字。37函數(shù)聲明我們把正在討論的這類函數(shù)聲明稱為函數(shù)原型(funct函數(shù)聲明C99遵循這樣的規(guī)則:在調(diào)用一個函數(shù)之前,必須先對其進(jìn)行聲明或定義。調(diào)用函數(shù)時,如果此前編譯器未見到該函數(shù)的聲明或定義,會導(dǎo)致出錯。38函數(shù)聲明C99遵循這樣的規(guī)則:在調(diào)用一個函數(shù)之前,必須先對其實際參數(shù)在C語言中,實際參數(shù)是通過值傳遞的:調(diào)用函數(shù)時,計算出每個實際參數(shù)的值并且把它賦值給相應(yīng)的形式參數(shù)。在函數(shù)執(zhí)行過程中,對形式參數(shù)的改變不會影響實際參數(shù)的值,這是因為形式參數(shù)中包含的是實際參數(shù)值的副本。39實際參數(shù)在C語言中,實際參數(shù)是通過值傳遞的:調(diào)用函數(shù)時,計算實際參數(shù)實際參數(shù)按值傳遞既有利也有弊。既然形式參數(shù)的修改不會影響到相應(yīng)的實際參數(shù),那么可以把形式參數(shù)作為函數(shù)內(nèi)的變量來使用,因此可以減少真正需要的變量的數(shù)量。40實際參數(shù)實際參數(shù)按值傳遞既有利也有弊。40實際參數(shù)思考下面這個函數(shù),此函數(shù)用來計算數(shù)x的n次冪: intpower(intx,intn) { inti,result=1;

for(i=1;i<=n;i++) result=result*x;

returnresult; }41實際參數(shù)思考下面這個函數(shù),此函數(shù)用來計算數(shù)x的n次冪:41實際參數(shù)因為n只是原始指數(shù)的副本,所以可以在函數(shù)體內(nèi)修改它,因此就不需要使用變量i了: intpower(intx,intn) { intresult=1;

while(n-->0) result=result*x;

returnresult; }42實際參數(shù)因為n只是原始指數(shù)的副本,所以可以在函數(shù)體內(nèi)修改它,實際參數(shù)C語言關(guān)于實際參數(shù)按值傳遞的要求使它很難編寫某些類型的函數(shù)。假設(shè)我們需要一個函數(shù),它將把double型的值分解成整數(shù)部分和小數(shù)部分。因為函數(shù)無法返回兩個數(shù),所以可以嘗試把兩個變量傳遞給函數(shù)并且修改它們: voiddecompose(doublex,longint_part, doublefrac_part) { int_part=(long)x; frac_part=x-int_part; }43實際參數(shù)C語言關(guān)于實際參數(shù)按值傳遞的要求使它很難編寫某些類型實際參數(shù)假設(shè)采用下面的方法調(diào)用這個函數(shù): decompose(3.14159,i,d);可惜的是,變量i和d不會因為賦值給int_part和frac_part而受到影響。第11章中將闡述如何使decompose函數(shù)正確地工作。44實際參數(shù)假設(shè)采用下面的方法調(diào)用這個函數(shù): decompose實際參數(shù)的轉(zhuǎn)換C語言允許在實際參數(shù)的類型與形式參數(shù)的類型不匹配的情況下進(jìn)行函數(shù)調(diào)用。管理如何轉(zhuǎn)換實際參數(shù)的規(guī)則與編譯器是否在調(diào)用前遇到函數(shù)(或者函數(shù)的完整定義)的原型有關(guān):45實際參數(shù)的轉(zhuǎn)換C語言允許在實際參數(shù)的類型與形式參數(shù)的類型不匹實際參數(shù)的轉(zhuǎn)換編譯器在調(diào)用前遇到原型。就像使用賦值一樣,每個實際參數(shù)的值被隱式地轉(zhuǎn)換成相應(yīng)形式參數(shù)的類型。例如:如果把int類型的實際參數(shù)傳遞給期望得到double型數(shù)據(jù)的函數(shù),那么會自動把實際參數(shù)轉(zhuǎn)換成double類型。46實際參數(shù)的轉(zhuǎn)換編譯器在調(diào)用前遇到原型。46實際參數(shù)的轉(zhuǎn)換編譯器在調(diào)用前沒有遇到原型。編譯器執(zhí)行默認(rèn)的實際參數(shù)提升:把float型的實際參數(shù)轉(zhuǎn)換成double類型。執(zhí)行整數(shù)的提升,即把char型和short型的實際參數(shù)轉(zhuǎn)換成int型(在C99中實現(xiàn)了整數(shù)提升)。47實際參數(shù)的轉(zhuǎn)換編譯器在調(diào)用前沒有遇到原型。47實際參數(shù)的轉(zhuǎn)換依賴默認(rèn)的實際參數(shù)提升是危險的。例如: #include<stdio.h>

intmain(void) { doublex=3.0; printf("Square:%d\n",square(x));

return0; }

intsquare(intn) { returnn*n; }在調(diào)用square函數(shù)時,編譯器沒有遇到原型,所以不知道該函數(shù)期望有int類型的實際參數(shù)。48實際參數(shù)的轉(zhuǎn)換依賴默認(rèn)的實際參數(shù)提升是危險的。48實際參數(shù)的轉(zhuǎn)換取而代之的,編譯器在變量x上執(zhí)行了沒有效果的默認(rèn)的實際參數(shù)提升。因為square函數(shù)期望有int類型的實際參數(shù),但是卻獲得了double類型值,所以square函數(shù)將產(chǎn)生無效的結(jié)果。把square’s的實際參數(shù)強(qiáng)制轉(zhuǎn)換為正確的類型可解決這個問題: printf("Square:%d\n",square((int)x));當(dāng)然更好的解決方案是在調(diào)用square函數(shù)前提供其原型。在C99中,調(diào)用square之前不提供聲明或者定義是錯誤的。49實際參數(shù)的轉(zhuǎn)換取而代之的,編譯器在變量x上執(zhí)行了沒有效果的默數(shù)組型實際參數(shù)當(dāng)形式參數(shù)是一維數(shù)組時,可以不說明數(shù)組的長度: intf(inta[])/*nolengthspecified*/ { … }C語言沒有為函數(shù)提供任何簡便的方法來確定傳遞給它的數(shù)組的長度。但是,如果函數(shù)需要,必須把長度作為額外的實際參數(shù)提供出來。50數(shù)組型實際參數(shù)當(dāng)形式參數(shù)是一維數(shù)組時,可以不說明數(shù)組的長度:數(shù)組型實際參數(shù)例子: intsum_array(inta[],intn) { inti,sum=0;

for(i=0;i<n;i++) sum+=a[i];

returnsum; }因為sum_array需要知道a的長度,所以我們需要將其作為第二個參數(shù)提供出來。51數(shù)組型實際參數(shù)例子:51數(shù)組型實際參數(shù)sum_array函數(shù)的原型形式如下: intsum_array(inta[],intn);通常情況下,如果愿意可以忽略形式參數(shù)的名字: intsum_array(int[],int);52數(shù)組型實際參數(shù)sum_array函數(shù)的原型形式如下:52數(shù)組型實際參數(shù)在調(diào)用sum_array函數(shù)時,第一個參數(shù)是數(shù)組的名字,而第二個參數(shù)是這個數(shù)組的長度。 #defineLEN100

intmain(void) { intb[LEN],total; … total=sum_array(b,LEN); … }注意,在把數(shù)組名傳遞給函數(shù)時,不要在數(shù)組名的后邊放置方括號: total=sum_array(b[],LEN);/***WRONG***/53數(shù)組型實際參數(shù)在調(diào)用sum_array函數(shù)時,第一個參數(shù)是數(shù)數(shù)組型實際參數(shù)函數(shù)無法檢測通過傳遞是否獲得了正確的數(shù)組長度。人們可以利用這個事實,方法是告訴函數(shù)數(shù)組比實際小得多。假設(shè)雖然數(shù)組b可以擁有100個元素,但是實際僅存儲了50個元素。通過書寫下列語句可以對數(shù)組的前50個元素進(jìn)行求和: total=sum_array(b,50);54數(shù)組型實際參數(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ù)組型實際參數(shù)函數(shù)可以改變數(shù)組型形式參數(shù)的元素,且改變會在相應(yīng)的實際參數(shù)中體現(xiàn)出來。下面的函數(shù)通過在每個數(shù)組元素中存儲0來修改數(shù)組: voidstore_zeros(inta[],intn) { inti;

for(i=0;i<n;i++) a[i]=0; }56數(shù)組型實際參數(shù)函數(shù)可以改變數(shù)組型形式參數(shù)的元素,且改變會在相數(shù)組型實際參數(shù)調(diào)用store_zeros: store_zeros(b,100);數(shù)組型實際參數(shù)的元素可以修改似乎與C語言中實際參數(shù)的值傳遞相矛盾。第12章中將解釋這其實不矛盾。57數(shù)組型實際參數(shù)調(diào)用store_zeros:57數(shù)組型實際參數(shù)如果形式參數(shù)是多維數(shù)組,則只有第一維的長度可以省略。如果我們修改sum_array函數(shù),使得a是個二維數(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ù)組型實際參數(shù)不能傳遞具有任意列數(shù)的多維數(shù)組是很討厭的。但我們經(jīng)??梢酝ㄟ^使用指針數(shù)組來解決這個問題。C99中的變長數(shù)組形式參數(shù)為上述問題提供了一個更好的解決方案。59數(shù)組型實際參數(shù)不能傳遞具有任意列數(shù)的多維數(shù)組是很討厭的。59變長型數(shù)組形式參數(shù)(C99)C99允許使用變長型數(shù)組作為形式參數(shù)??紤]sum_array函數(shù): intsum_array(inta[],intn) { … }

這樣的定義使得n和數(shù)組a的長度之間沒有直接的聯(lián)系。盡管函數(shù)體會將n看作數(shù)組a的長度,但是數(shù)組的實際長度有可能比n大或者小。60變長型數(shù)組形式參數(shù)(C99)C99允許使用變長型數(shù)組作為形式變長型數(shù)組形式參數(shù)(C99)如果使用變長數(shù)組形式參數(shù),我們可以明確說明數(shù)組a的長度就是n: intsum_array(intn,inta[n]) { … }第一個參數(shù)(n)的值確定了第二個參數(shù)(a)的長度。注意這里交換了形式參數(shù)的順序,使用變長數(shù)組形式參數(shù)時,參數(shù)的順序很重要。61變長型數(shù)組形式參數(shù)(C99)如果使用變長數(shù)組形式參數(shù),我們可變長型數(shù)組形式參數(shù)(C99)對于新版本的sum_array函數(shù),其函數(shù)原型有好幾種寫法:一種寫法是使其看起來跟函數(shù)定義一樣: int

sum_array(int

n,

int

a[n]);

/*

Version1

*/另一種寫法是用*(星號)取代數(shù)組長度: int

sum_array(int

n,

int

a[*]);

/*

Version2a

*/62變長型數(shù)組形式參數(shù)(C99)對于新版本的sum_array函變長型數(shù)組形式參數(shù)(C99)使用*的理由是:函數(shù)聲明時,形式參數(shù)的名字是可選的。如果第一個參數(shù)定義被省略了,那么就沒有辦法說明數(shù)組的長度是n,而*的使用則為我們提供了一個線索——數(shù)組的長度與形式參數(shù)列表中前面的參數(shù)相關(guān): int

sum_array(int,

int

[*]);

/*

Version

2b

*/63變長型數(shù)組形式參數(shù)(C99)使用*的理由是:函數(shù)聲明時,形式變長型數(shù)組形式參數(shù)(C99)另外,方括號中為空也是合法的。在聲明數(shù)組參數(shù)中我們經(jīng)常這么做: intsum_array(intn,inta[]);/*Version3a*/ intsum_array(int,int[]);/*Version3b*/讓括號為空不是一個好的選擇,因為這樣并沒有說明n和a的關(guān)系。64變長型數(shù)組形式參數(shù)(C99)另外,方括號中為空也是合法的。在變長型數(shù)組形式參數(shù)(C99)一般來說,變長數(shù)組形式參數(shù)的長度可以是任意表達(dá)式。假設(shè)要編寫一個函數(shù)來連接數(shù)組a和b,結(jié)果存到第三個數(shù)組c中: intconcatenate(int

m,

int

n,

int

a[m],

int

b[n], int

c[m+n]) { … }這里用于指定數(shù)組C長度的表達(dá)式只用到了另外兩個參數(shù);但一般來說,該表達(dá)式可以使用函數(shù)外部的變量,甚至可以調(diào)用其他函數(shù)。65變長型數(shù)組形式參數(shù)(C99)一般來說,變長數(shù)組形式參數(shù)的長度變長型數(shù)組形式參數(shù)(C99)一維變長數(shù)組做形式參數(shù)的用途往往是有限的。一維變長數(shù)組形式參數(shù)通過指定數(shù)組參數(shù)的長度使得函數(shù)的聲明和定義更具描述性。但是,由于沒有進(jìn)行額外的錯誤檢測,數(shù)組參數(shù)仍然有可能太長或太短。66變長型數(shù)組形式參數(shù)(C99)一維變長數(shù)組做形式參數(shù)的用途往往變長型數(shù)組形式參數(shù)(C99)如果變長數(shù)組參數(shù)是多維的則更加實用。如果使用變長數(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變長型數(shù)組形式參數(shù)(C99)如果變長數(shù)組參數(shù)是多維的則更加實變長型數(shù)組形式參數(shù)(C99)這個函數(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ù)(C99)這個函數(shù)的原型可以是以下幾種:6在數(shù)組參數(shù)聲明中使用static(C99)C99允許在數(shù)組參數(shù)聲明中使用關(guān)鍵字static。在下面這個例子中,將static放在數(shù)字3之前表明數(shù)組a的長度至少可以保證是3: intsum_array(inta[static3],intn) { … }69在數(shù)組參數(shù)聲明中使用static(C99)C99允許在數(shù)組在數(shù)組參數(shù)聲明中使用static(C99)使用static不會對程序的行為有任何影響。static的存在只不過是一個“提示”,C編譯器可以據(jù)此生成更快的指令來訪問數(shù)組。如果數(shù)組參數(shù)是多維的,static僅可用于第一維。70在數(shù)組參數(shù)聲明中使用static(C99)使用static復(fù)合字面量(C99)讓我們再來看看最初的sum_array函數(shù)。當(dāng)調(diào)用sum_array時,第一個實際參數(shù)通常是數(shù)組名。例如: intb[]={3,0,3,4,1}; total=sum_array(b,5);這樣寫的唯一問題是需要把b作為一個變量聲明,并在調(diào)用前進(jìn)行初始化。如果b不作它用,而是僅為了調(diào)用sum_array來創(chuàng)建它,則有些浪費(fèi)。71復(fù)合字面量(C99)讓我們再來看看最初的sum_array復(fù)合字面量(C99)在C99中,可以使用復(fù)合字面量(compoundliteral)來避免該問題:復(fù)合字面量是通過指定其包含的元素而創(chuàng)建的沒有名字的數(shù)組。下面調(diào)用sum_array函數(shù),第一個參數(shù)(黑體)就是一個復(fù)合字面量: total

=

sum_array((int

[]){3,

0,

3,

4,

1},

5);這里沒有對數(shù)組的長度進(jìn)行特別的說明,是由復(fù)合字面量的元素個數(shù)決定的。當(dāng)然,也可以對長度做準(zhǔn)確說明 (int[4]){1,9,2,1}

等同于 (int[]){1,9,2,1}72復(fù)合字面量(C99)在C99中,可以使用復(fù)合字面量(com復(fù)合字面量(C99)復(fù)合字面量類似于應(yīng)用于初始化式的強(qiáng)制轉(zhuǎn)換。事實上,復(fù)合字面量和初始化式遵守同樣的規(guī)則。復(fù)合字面量可以包含指示符,就像指定初始化式一樣;可以不提供完全的初始化(未初始化的元素默認(rèn)被初始化為零)。例如,復(fù)合字面量(int[10]){8,6}有10個元素;前兩個元素的值為8和6,剩下的元素值為0。73復(fù)合字面量(C99)復(fù)合字面量類似于應(yīng)用于初始化式的強(qiáng)制轉(zhuǎn)復(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}74復(fù)合字面量(C99)函數(shù)內(nèi)部創(chuàng)建的復(fù)合字面量可以包含任意的return語句非void的函數(shù)必須使用return語句來說明將要返回的值。return語句有如下格式: return表達(dá)式

;表達(dá)式經(jīng)常只是常量或變量: return0; returnstatus;也可能是更加復(fù)雜的表達(dá)式: returnn>=0?n:0;75return語句非void的函數(shù)必須使用return語句來說return語句如果return語句中表達(dá)式的類型和函數(shù)的返回類型不匹配,那么系統(tǒng)將會把表達(dá)式的類型隱式轉(zhuǎn)換成返回類型。例如,如果聲明函數(shù)返回int型值,但是return語句包含double型表達(dá)式,那么系統(tǒng)將會把表達(dá)式的值轉(zhuǎn)換成int型。76return語句如果return語句中表達(dá)式的類型和函數(shù)的返return語句如果沒有給出表達(dá)式,return語句可以出現(xiàn)在返回類型為void的函數(shù)中: return;/*

return

in

a

void

function

*/例子: voidprint_int(inti) { if(i<0) return; printf("%d",i); }

77return語句如果沒有給出表達(dá)式,return語句可以出現(xiàn)return語句Return語句可以出現(xiàn)在void函數(shù)的末尾: voidprint_pun(void) { printf("To

C,

or

not

to

C:

that

is

the

question.\n"); return;/*OK,butnotneeded*/ }

這里return語句不是必需的。如果非void函數(shù)到達(dá)了函數(shù)體的末尾(也就是說沒有執(zhí)行return語句),那么如果程序試圖使用函數(shù)的返回值,其行為將是未定義的。78return語句Return語句可以出現(xiàn)在void函數(shù)的末尾程序終止正常情況下,main的返回類型是int: intmain(void) { … }以往的C程序常常省略main的返回類型,這其實是利用了返回類型默認(rèn)為int類型的傳統(tǒng): main() { … }79程序終止正常情況下,main的返回類型是int:79程序終止省略函數(shù)的返回類型在C99中是非法的,所以應(yīng)避免這么做。省略main函數(shù)參數(shù)列表中的void是合法的,但是最好顯式地表明main函數(shù)沒有參數(shù)。80程序終止省略函數(shù)的返回類型在C99中是非法的,所以應(yīng)避免這么程序終止main數(shù)返回的值是狀態(tài)碼,在程序終止時可以檢測到狀態(tài)碼。如果程序正常終止,main函數(shù)應(yīng)該返回0。為了說明異常終止,main函數(shù)應(yīng)該返回非0的值。確信每個C程序都返回狀態(tài)碼是一個很好的實踐。81程序終止main數(shù)返回的值是狀態(tài)碼,在程序終止時可以檢測到狀exit函數(shù)在main函數(shù)中執(zhí)行return語句是終止程序的一種方法。另一種方法是調(diào)用exit函數(shù),此函數(shù)屬于<stdlib.h>。傳遞給exit函數(shù)的實際參數(shù)和main函數(shù)的返回值具有相同的含義:兩者都說明程序終止時的狀態(tài)。為了說明正常終止,傳遞0: exit(0);/*normaltermination*/82exit函數(shù)在main函數(shù)中執(zhí)行return語句是終止程序的exit函數(shù)因為0有些模糊,所以C語言允許用傳遞EXIT_SUCCESS來代替(效果是相同的): exit(EXIT_SUCCESS);傳遞EXIT_FAILURE表示異常終止: exit(EXIT_FAILURE);EXIT_SUCCESS和EXIT_FAILURE是定義在<stdlib.h>中的宏。EXIT_SUCCESS和EXIT_FAILURE的值都是事先定義的,通常分別為0和1。83exit函數(shù)因為0有些模糊,所以C語言允許用傳遞EXIT_Sexit函數(shù)Main函數(shù)中的語句 return表達(dá)式;

等價于 exit(表達(dá)式);return語句和exit函數(shù)之間的差異是:不管哪個函數(shù)調(diào)用exit函數(shù)都會導(dǎo)致程序終止。Return語句僅當(dāng)由main函數(shù)調(diào)用時才會導(dǎo)致程序終止。84exit函數(shù)Main函數(shù)中的語句84遞歸如果函數(shù)調(diào)用它本身,那么此函數(shù)就是遞歸的(recursive)。例如,利用公式n!=n×(n–1)!,下面的函數(shù)可以遞歸地計算出n!的結(jié)果: intfact(intn) { if(n<=1) return1; else returnn*fact(n-1); }85遞歸如果函數(shù)調(diào)用它本身,那么此函數(shù)就是遞歸的(recursi為了了解遞歸的工作原理,一起來跟蹤下面這個語句的執(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為了了解遞歸的工作原理,一起來跟蹤下面這個語句的執(zhí)行:遞歸8遞歸下面的遞歸函數(shù)利用公式xn=x×xn–1計算出xn的值: intpower(intx,intn) { if(n==0) return1; else returnx*power(x,n-1); }87遞歸下面的遞歸函數(shù)利用公式xn=x×xn–1計算出x遞歸通過把條件表達(dá)式放入return語句中,可以精簡power函數(shù): intpower(intx,intn) { return

n

==

0

?

1

:

x

*

power(x,

n

-

1); }一旦被調(diào)用,fact函數(shù)和power函數(shù)都會仔細(xì)地測試“終止條件”。所有遞歸函數(shù)都需要某些類型的終止條件。88遞歸通過把條件表達(dá)式放入return語句中,可以精簡powe快速排序算法遞歸對要求函數(shù)調(diào)用自身兩次或多次的復(fù)雜算法非常有幫助。實際上,遞歸經(jīng)常作為分治法(divide-and-conquer)技術(shù)的結(jié)果自然地出現(xiàn)。這種稱為分治法的算法設(shè)計方法把一個大問題劃分成多個較小的問題,然后采用相同的算法分別解決這些小問題。

89快速排序算法遞歸對要求函數(shù)調(diào)用自身兩次或多次的復(fù)雜算法非常有分治法的經(jīng)典示例就是流行的排序算法—快速排序(quicksort)。假設(shè)要排序的數(shù)組的下標(biāo)從1到n。

快速排序算法(1)選擇數(shù)組元素e(作為“分割元素”),然后重新排列數(shù)組使得元素從1一直到i-1都是小于或等于元素e的,元素i包含e,而元素從i+1一直到n都是大于或等于e的。(2)通過遞歸地采用快速排序方法,對從1到i-1的元素進(jìn)行排序。(3)通過遞歸地采用快速排序方法,對從i+1到n的元素進(jìn)行排序。

快速排序算法90分治法的經(jīng)典示例就是流行的排序算法—快速排序(quickso快速排序算法顯然快速排序中的第1步是很關(guān)鍵的。有許多種方法可以用來分割數(shù)組。我們采用的方法是很容易理解的,但是它不是特別有效。該算法依賴于兩個名為low和high的標(biāo)記,這兩個標(biāo)記用來跟蹤數(shù)組內(nèi)的位置。91快速排序算法顯然快速排序中的第1步是很關(guān)鍵的。91快速排序算法開始,low指向數(shù)組中的第一個元素,而high指向末尾元素。首先把第一個元素(分割元素)復(fù)制給其他地方的一個臨時存儲單元,從而在數(shù)組中留出一個“空位”。接下來,從右向左移動high,直到high指向小于分割元素的數(shù)時停止。然后把這個數(shù)復(fù)制給low指向的空位,這將產(chǎn)生一個新的空位(high指向的)?,F(xiàn)在從左向右移動low,尋找大于分割元素的數(shù)。在找到時,把這個找到的數(shù)復(fù)制給high指向的空位。重復(fù)執(zhí)行此過程,交替操作low和high直到兩者在數(shù)組中間的某處相遇時停止。此時,兩個標(biāo)記都將指向空位:只要把分割元素復(fù)制給空位就夠了。92快速排序算法開始,low指向數(shù)組中的第一個元素,而high指快速排序算法分割數(shù)組的示例:93快速排序算法分割數(shù)組的示例:93快速排序算法從最后一個圖可以看出,分割元素左側(cè)的所有元素都小于或等于12,而其右側(cè)的所有元素都大于或等于12。既然己經(jīng)分割了數(shù)組,那么就可以使用快速排序法對數(shù)組的前4個元素(10,3,6,和7)和后2個元素(15和18)進(jìn)行遞歸快速排序了。94快速排序算法從最后一個圖可以看出,分割元素左側(cè)的所有元素都小程序:快速排序讓我們先來開發(fā)一個名為quicksort的遞歸函數(shù),此函數(shù)采用快速排序算法對整型數(shù)組進(jìn)行排序。程序qsort.c將10個數(shù)讀入一個數(shù)組,調(diào)用quicksort函數(shù)來對數(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ù)中。95程序:快速排序讓我們先來開發(fā)一個名為quicksort的遞歸qsort.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;}96qsort.c96voidquicksort(inta[],intlow,inthigh){intmiddle;

if(low>=high)return;middle=split(a,low,high);quicksort(a,low,middle-1);quicksort(a,middle+1,high);}97voidquicksort(inta[],intlointsplit(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)

溫馨提示

  • 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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論