1、課程大綱
2、第1部份第9課:函數(shù)
3、第1部份第10課預(yù)告: 練習(xí)題+習(xí)作
我們的課程分為4大部份,每個(gè)部份結(jié)束后都會(huì)有練習(xí)題,并會(huì)公布答案。還會(huì)帶大家用C語言編寫3個(gè)游戲。
C語言編程基礎(chǔ)知識
甚么是編程?
工欲善其事,必先利其器
你的第1個(gè)程序
變量的世界
運(yùn)算那點(diǎn)事
條件表達(dá)式
循環(huán)語句
實(shí)戰(zhàn):第1個(gè)C語言小游戲
函數(shù)
練習(xí)題
習(xí)作:完善第1個(gè)C語言小游戲
模塊化編程
進(jìn)擊的指針,C語言王牌
數(shù)組
字符串
預(yù)處理
創(chuàng)建你自己的變量類型
文件讀寫
動(dòng)態(tài)分配
實(shí)戰(zhàn):“懸掛小人”游戲
安全的文本輸入
練習(xí)題
習(xí)作:用自己的語言解釋指針
安裝SDL
創(chuàng)建窗口和畫布
顯示圖象
事件處理
實(shí)戰(zhàn):“超級瑪麗推箱子”游戲
掌握時(shí)間的使用
用SDL_ttf編輯文字
用FMOD控制聲音
實(shí)戰(zhàn):可視化的聲音譜線
練習(xí)題
鏈表
堆,棧和隊(duì)列
哈希表
練習(xí)題
最近各種社交媒體和新聞都被姚貝娜和杰倫刷屏了。1個(gè)使人惋惜,1個(gè)使人高興。
我覺得怎樣也得讓編程來刷1下屏吧。哈哈,開玩笑的,今天就用函數(shù)來刷屏好了。
說是刷屏,其實(shí)也是翻頁,由于這1課我們將會(huì)用函數(shù)這個(gè)重中之重來結(jié)束《C語言探索之旅》的第1部份(基礎(chǔ)部份),而第2部份將要迎接我們的就是C語言的高級技術(shù)了。刷走了1個(gè)部份,準(zhǔn)備迎接新的挑戰(zhàn)。
第2部份會(huì)比較難哦,不過不用擔(dān)心,我們1點(diǎn)點(diǎn)學(xué)習(xí)。只要方向?qū)Γ匣〞r(shí)間,C語言其實(shí)不可怕。
這1課里我們也會(huì)給大家講C語言程序所基于的原則。
我們將要學(xué)習(xí)如何將程序分塊管理,有點(diǎn)像樂高積木...
其實(shí)所有C語言的大型程序都是小程序塊的集合,而這些小程序塊我們稱之為函數(shù)。在面向?qū)ο蟮恼Z言如Java里面,函數(shù)又被稱為方法,固然這里我們只討論C語言(面向進(jìn)程的語言),不討論面向?qū)ο蟮恼Z言,sorry,我又空話了... 好吧,我們只面向C語言這個(gè)對象。
函數(shù)的創(chuàng)建和調(diào)用
在之前的課程中我們已學(xué)過所有的C語言程序都是由main函數(shù)開始運(yùn)行的。那時(shí)候我們也展現(xiàn)了1個(gè)概要圖,里面有1些術(shù)語:
最上面的部份我們稱之為“預(yù)處理指令”,很容易辨識,由于以#號開頭,而且總是放在程序的最前面。
下面的部份就是我們要學(xué)習(xí)的函數(shù)了,這里的示例是main函數(shù)。
前面說過,C語言的程度都是以main函數(shù)為入口函數(shù)的。1個(gè)C程序要運(yùn)行,必須要有main函數(shù)。只不過,目前為止我們寫的所有程序 包括上1課的小游戲,也只是在main函數(shù)里面搗鼓而已,我們還沒跳出過main函數(shù)過。沒辦法,有錢任性,沒錢只能…
那你要問了:這樣不好嗎?
答案是:其實(shí)不是說這樣不好,但這其實(shí)不是C程序員平時(shí)所做的。幾近沒有程序員會(huì)只在main函數(shù)的大括號內(nèi)部寫代碼。到目前為止我們所寫的程序都還比較短小,但是想象1下如果程序變得很大,代碼幾千幾萬乃至上百萬行,難道我們還把這些代碼都塞在main函數(shù)里面嗎?
所以我們現(xiàn)在來學(xué)習(xí)如何更好地計(jì)劃我們的程序。我們要學(xué)習(xí)將程序分成很多小塊,就像樂高積木的每個(gè)小塊1樣,這些小塊搭起來卻可以組成很多好玩的形狀。這些程序小塊我們稱其為函數(shù)。
1個(gè)函數(shù)會(huì)履行某些操作,并返回1個(gè)值。程序就是1個(gè)代碼序列,負(fù)責(zé)完成特定的任務(wù)。
我們說1個(gè)函數(shù)有輸入和輸出,以下圖所示:
可以把函數(shù)想象成1臺(tái)制作香腸的機(jī)器,在輸入那1頭你把豬裝進(jìn)去,輸出那1頭就出來香腸了。這酸爽。
當(dāng)我們在程序中調(diào)用1個(gè)函數(shù)的時(shí)候,會(huì)順次產(chǎn)生3個(gè)步驟:
輸入:給函數(shù)傳入1些信息(借著給函數(shù)1些參數(shù))
運(yùn)算:因著輸入里傳進(jìn)去的信息,函數(shù)就能夠完成特定任務(wù)了
輸出:做完運(yùn)算后,函數(shù)會(huì)返回1個(gè)結(jié)果。我們稱其為輸出或返回值
舉個(gè)實(shí)際例子,比如我們有個(gè)函數(shù)叫做multipleTwo,作用是將輸入乘以2,以下所示:
函數(shù)的目的是為了讓源代碼更加結(jié)構(gòu)分明,也節(jié)省源代碼數(shù)目,由于我們就不用每次都輸入重復(fù)的代碼片斷而只需要調(diào)用函數(shù)就行了。
再假想1下:以后我們可能會(huì)想要?jiǎng)?chuàng)建1個(gè)叫showWindow(顯示窗口)的函數(shù),作用是在屏幕上顯示1個(gè)窗口。1旦函數(shù)寫好以后(固然寫的進(jìn)程是最難的),我們就只需要說:“那個(gè)誰,給我去打開1個(gè)窗口”,showWindow函數(shù)就會(huì)為我們在屏幕上顯示1個(gè)窗口。我們也能夠?qū)?個(gè)displayPersonage的函數(shù),作用是為我們在屏幕上顯示1個(gè)游戲人物。
函數(shù)的構(gòu)成
我們在之前的課中已接觸過函數(shù)了,就是非常重要的main函數(shù)。但是還是需要我們來介紹1下1個(gè)函數(shù)的構(gòu)成究竟是怎樣樣的。
下面是函數(shù)的語義學(xué)的結(jié)構(gòu),這是1個(gè)需要了解的模板:
類型 函數(shù)名(參數(shù))
{
// 函數(shù)體,在這里插入指令
}
關(guān)于這個(gè)模板我們需要掌握4點(diǎn):
函數(shù)類型:對應(yīng)輸出類型,也能夠把其看作函數(shù)的類型。和變量類似,函數(shù)也有類型,這類型取決于函數(shù)返回值的類型。如果1個(gè)函數(shù)返回1個(gè)浮點(diǎn)數(shù)(帶小數(shù)點(diǎn)的),那末自然我們會(huì)將函數(shù)類型定為float或double;如果返回整數(shù),那末我們1般會(huì)將類型定為int或long。但是我們也能夠創(chuàng)建不返回任何值的函數(shù)。
函數(shù)名:這是你的函數(shù)的名字。你可以給你的函數(shù)起任意名字,只要遵從給變量命名的相同的規(guī)則就好。
函數(shù)的參數(shù)(對應(yīng)輸入):參數(shù)位于函數(shù)名以后的圓括號內(nèi)。這些參數(shù)是函數(shù)要用來做操作(運(yùn)算)的數(shù)據(jù)。你可以給函數(shù)傳入任意數(shù)量的參數(shù),也能夠不傳入任何參數(shù)。
函數(shù)體:大括號規(guī)定了函數(shù)的起始和結(jié)束范圍。在大括號中你可以寫入任意多的指令。對上面的multipleTwo函數(shù),需要寫入將輸入的數(shù)字乘以2的相干操作指令。
根據(jù)函數(shù)類型,函數(shù)可以分為兩類:
返回1個(gè)值的函數(shù),這樣的函數(shù),我們將其類型定為對應(yīng)的值的類型(char,int,long,double等)
不返回任何值的函數(shù),這樣的函數(shù),我們將其類型定為void(void在英語中是“空的,無效的”之意)
創(chuàng)建函數(shù)
我們不要再遲延了,馬上給出1個(gè)實(shí)例。用的還是我們上面提過的multipleTwo這個(gè)函數(shù):這個(gè)函數(shù)的輸入是1個(gè)整型int,輸出也是int類型的數(shù)。
int multipleTwo(int number)
{
int result = 0;
result = 2 * number; // 我們將提供的數(shù)乘以2
return result; // 我們將2倍的數(shù)返回
}
這就是你的第1個(gè)除main之外的函數(shù),自豪不?
return result;
這句話1般放在函數(shù)體的最后,用于返回1個(gè)值。這句話意味著:“函數(shù)你給我停下,然后返回這個(gè)值”。這里的result必須是int類型的,由于函數(shù)類型是int,所以返回值也必須是int類型。
result這個(gè)變量是在multipleTwo函數(shù)中聲明/創(chuàng)建的,所以它只能在這個(gè)函數(shù)里面用,不能在另外一個(gè)函數(shù)比如main中使用,所以是multipleTwo函數(shù)的私有變量。
但上面的代碼是否是最簡單的呢?
不是,還可以簡化,以下:
int multipleTwo(int number)
{
return 2 * number;
}
上面的代碼做的是1樣的事情,寫起來也更簡單,函數(shù)體內(nèi)只有1句話。
通常來講,我們寫的函數(shù)都會(huì)有多個(gè)變量,以便做運(yùn)算,multipleTwo這個(gè)函數(shù)算是相當(dāng)簡單了。
多個(gè)參數(shù),或沒有參數(shù)
多個(gè)參數(shù)
我們的multipleTwo函數(shù)只有1個(gè)參數(shù),但是我們也能夠創(chuàng)建有幾個(gè)參數(shù)的函數(shù),比以下面這個(gè)加法函數(shù)addition:
int addition(int a, int b)
{
return a + b;
}
可以看到,只需要用1個(gè)逗號來分隔參數(shù)就行了。
沒有參數(shù)
有些函數(shù),雖然不太常見,可能會(huì)沒有參數(shù)。例如1個(gè)用來顯示Hello(你好)的函數(shù):
void hello()
{
printf("Hello");
}
如上所見,這個(gè)函數(shù)沒有任何參數(shù),另外,可以看到我們還把函數(shù)類型定為了void,就是空,所以也沒有return語句用于返回1個(gè)值,所以這個(gè)函數(shù)也沒有返回值。徹徹底底的黑5類…
調(diào)用函數(shù)
現(xiàn)在我們來看1個(gè)程序,溫習(xí)1下我們之前學(xué)的內(nèi)容。
我們要用到我們的multipleTwo函數(shù),來計(jì)算1個(gè)數(shù)的兩倍的值。
我們暫時(shí)把multipleTwo函數(shù)寫在main函數(shù)之前,如果放在main函數(shù)以后會(huì)出錯(cuò),以后的課程我們會(huì)解釋為何。
#include <stdio.h>
int multipleTwo(int number)
{
return 2 * number;
}
int main(int argc, char *argv[])
{
int initial = 0, twice = 0;
printf("請輸入1個(gè)整數(shù)... ");
scanf("%d", &initial);
twice = multipleTwo(initial);
printf("這個(gè)數(shù)的兩倍是 %d ", twice);
return 0;
}
我們的程序是從main函數(shù)開始運(yùn)行的,這個(gè)大家已知道了。
我們首先要求用戶輸入1個(gè)整數(shù),將其值傳遞給multipleTwo函數(shù),并且把multipleTwo函數(shù)的返回值賦給twice這個(gè)變量。
仔細(xì)看下面這1行,這是我們最關(guān)心的1行代碼,由于正是這1行調(diào)用了我們的multipleTwo函數(shù)。
twice = multipleTwo(initial);
在括號里,我們將變量initial當(dāng)作輸入傳遞給函數(shù),也正是這個(gè)變量,函數(shù)將要用于其內(nèi)部的處理。
這個(gè)函數(shù)返回1個(gè)值,這個(gè)值我們賦給twice這個(gè)變量。
其實(shí)這1行中,我們就是命令電腦:“讓multipleTwo函數(shù)給我計(jì)算initial的兩倍的值,并且將結(jié)果貯存到twice這個(gè)變量中”。
詳細(xì)的分步解釋
或許對初學(xué)者,理解起來還是有些許困難。
不用擔(dān)心,我相信通過下面的分步解釋,大家會(huì)明白得更透徹。
這個(gè)特殊注釋的代碼向大家展現(xiàn)了程序的運(yùn)行順序:
#include <stdio.h>
int multipleTwo(int number) // 6
{
return 2 * number; // 7
}
int main(int argc, char *argv[]) // 1
{
int initial = 0, twice = 0; // 2
printf("請輸入1個(gè)整數(shù)... "); // 3
scanf("%d", &initial); // 4
twice = multipleTwo(initial); // 5
printf("這個(gè)數(shù)的兩倍是 %d ", twice); // 8
return 0; // 9
}
上面的編號表示了履行的順序:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
程序從main函數(shù)開始履行
在main函數(shù)中的命令1行1行地被履行
履行printf輸出
履行scanf讀入數(shù)據(jù),賦值給變量initial
讀入指令... 啊,調(diào)用multipleTwo函數(shù)了,因此程序跳到上面的multipleTwo函數(shù)中去履行
我們運(yùn)行multipleTwo函數(shù),并接受1個(gè)數(shù)作為輸入(number)
我們對number做運(yùn)算,并且結(jié)束multipleTwo函數(shù),return意味著函數(shù)的結(jié)束,并且返回1個(gè)值
我們重新回到main函數(shù)的下1條指令,用printf輸出
又1個(gè)return,這次是main函數(shù)的結(jié)束,因而全部程序運(yùn)行終了。
變量initial被傳值給multipleTwo的參數(shù)number(另外一個(gè)變量),稱為值傳遞。固然其實(shí)原理是做了1份變量initial的拷貝,把拷貝賦值給了number,這個(gè)值傳遞的概念以后學(xué)習(xí)指針那1章會(huì)再詳述。
這里如果我們把initial改名為number也是可以的,其實(shí)不會(huì)與函數(shù)multipleTwo的參數(shù)number沖突。由于參數(shù)number是屬于multipleTwo這個(gè)函數(shù)的專屬變量。
測試程序
下面是程序運(yùn)行起來的1個(gè)實(shí)例:
請輸入1個(gè)整數(shù)... 10
這個(gè)數(shù)的兩倍是 20
固然你沒必要將multipleTwo函數(shù)的返回值賦給1個(gè)變量,也能夠直接將multipleTwo函數(shù)的返回值傳遞給另外一個(gè)函數(shù),就好像multipleTwo(intial)是1個(gè)變量1般。
仔細(xì)看下面這個(gè)程序,跟上面幾近是1樣的,但是修改了最后1個(gè)printf的行動(dòng),我們也沒有使用twice這個(gè)變量,由于沒必要要:
#include <stdio.h>
int multipleTwo(int number)
{
return 2 * number;
}
int main(int argc, char *argv[])
{
int initial = 0, twice = 0;
printf("請輸入1個(gè)整數(shù)... ");
scanf("%d", &initial);
// 函數(shù)的結(jié)果(返回值)直接傳遞給printf函數(shù),而沒有通過第3方變量
printf("這個(gè)數(shù)的兩倍是 %d ", multipleTwo(initial));
return 0;
}
我們可以看到,這次的程序直接將multipleTwo函數(shù)的返回值傳遞給了printf函數(shù)。
當(dāng)程序運(yùn)行到這1行會(huì)產(chǎn)生甚么呢?
很簡單,電腦看到這1行是printf函數(shù),所以調(diào)用標(biāo)準(zhǔn)輸入輸出庫的printf函數(shù),向printf函數(shù)傳遞我們給的所有參數(shù)。第1個(gè)參數(shù)是要顯示的語句,第2個(gè)參數(shù)是1個(gè)整數(shù)。電腦又知道要把這個(gè)整數(shù)值傳遞給printf函數(shù),必須先調(diào)用multipleTwo函數(shù),所以它就乖乖地去調(diào)用multipleTwo函數(shù),做兩倍乘法運(yùn)算,并且直接把結(jié)果傳遞給printf函數(shù)。
這就是函數(shù)的層疊式調(diào)用,這樣做的好處是,1個(gè)函數(shù)可以按需調(diào)用另外一個(gè)函數(shù)。
只要愿意,我們的multipleTwo函數(shù)也能夠再調(diào)用其他的函數(shù),只要你肯寫,然后這個(gè)函數(shù)再調(diào)用其它函數(shù),順次類推。
這就是C語言程序所基于的原則。所有的代碼都是有計(jì)劃地組合在1起的,類似樂高積木。
最后,最艱巨確當(dāng)然是編寫函數(shù)了,1旦完成,你就只需要調(diào)用它就行了,不需要太擔(dān)心函數(shù)內(nèi)部所做的運(yùn)算。使用函數(shù)可以大大下降代碼的重復(fù)度,相信我,你會(huì)非常需要函數(shù)的。
1些函數(shù)的實(shí)例
如果1起學(xué)習(xí)過之前的課程,你應(yīng)當(dāng)會(huì)有這類印象:我就是個(gè)例子狂人。
是的,由于我很喜歡用實(shí)例來加深理解。我可不是甚么superhero(超級英雄),比如Iron Man,綠偉人,蟻人,等。
由于我覺得理論雖好,但如果只有理論,那我們就不能很好地掌握知識,而且不知道怎樣利用,那就很惋惜了。想起了“勁酒雖好,可不要貪杯哦”那句廣告詞…
所以下面我們會(huì)1起看幾個(gè)函數(shù)的實(shí)例,以便讀者對函數(shù)有更深入的了解。我們盡可能展現(xiàn)不同情況,使大家看到可能出現(xiàn)的各種函數(shù)類型。
歐元/人民幣轉(zhuǎn)換
最近歐元大貶值,已降到7左右了,我們就來寫1個(gè)函數(shù),用于轉(zhuǎn)換歐元到人民幣。
查了1下最新的匯率:
1歐元=7.1874人民幣元
#include <stdio.h>
double conversion(double euros)
{
double rmb = 0;
rmb = 7.1874 * euros;
return rmb;
}
int main(int argc, char *argv[])
{
printf("10 歐元 = %f 人民幣 ", conversion(10));
printf("50 歐元 = %f 人民幣 ", conversion(50));
printf("100 歐元 = %f 人民幣 ", conversion(100));
printf("200 歐元 = %f 人民幣 ", conversion(200));
return 0;
}
你也能夠?qū)?個(gè)人民幣轉(zhuǎn)換為歐元的小程序。
懲罰
接下來看1個(gè)函數(shù),這個(gè)函數(shù)不會(huì)返回任何值,所以類型是void。這個(gè)函數(shù)會(huì)根據(jù)傳入的參數(shù)在屏幕上顯示1定次數(shù)的信息。這個(gè)函數(shù)只有1個(gè)參數(shù),那就是顯示懲罰語句的次數(shù):
#include <stdio.h>
void punish(int lineNumber)
{
int i;
for (i = 0 ; i < lineNumber ; i++)
{
printf("我不應(yīng)當(dāng)有錢任性 ");
}
}
int main(int argc, char *argv[])
{
punish(5);
return 0;
}
顯示結(jié)果以下:
我不應(yīng)當(dāng)有錢任性
我不應(yīng)當(dāng)有錢任性
我不應(yīng)當(dāng)有錢任性
我不應(yīng)當(dāng)有錢任性
我不應(yīng)當(dāng)有錢任性
矩形面積
矩形的面積很容易計(jì)算:長 x 寬。
我們來寫1個(gè)求矩形面積的函數(shù),它有兩個(gè)參數(shù):矩形的長和矩形的寬。返回值是矩形的面積:
#include <stdio.h>
double rectangleArea(double length, double width)
{
return length * width;
}
int main(int argc, char *argv[])
{
printf("長是10,寬是5的矩形面積是 %f ", rectangleArea(10, 5));
printf("長是3.5,寬是2.5的矩形面積是 %f ", rectangleArea(3.5, 2.5));
printf("長是9.7,寬是4.2的矩形面積是 %f ", rectangleArea(9.7, 4.2));
return 0;
}
顯示結(jié)果:
長是10,寬是5的矩形面積是 50.000000
長是3.5,寬是2.5的矩形面積是 8.750000
長是9.7,寬是4.2的矩形面積是 40.740000
我們可以直接在函數(shù)里顯示 長,寬和計(jì)算所得的面積嗎?
固然可以。這樣的情況下,函數(shù)就沒必要返回任何值了,函數(shù)計(jì)算出矩形面積,然后直接顯示在屏幕上:
#include <stdio.h>
void rectangleArea(double length, double width)
{
double area = 0;
area = length * width;
printf("長為 %f 寬為 %f 的矩形的面積是 %f ", length, width, area);
}
int main(int argc, char *argv[])
{
rectangleArea(10, 5);
rectangleArea(3.5, 2.5);
rectangleArea(9.7, 4.2);
return 0;
}
我們可以看到,printf函數(shù)在函數(shù)體內(nèi)被調(diào)用,顯示的結(jié)果和之前把printf放在main函數(shù)里是1樣的。只不過我們用的方法不1樣罷了。
菜單
還記得之前的課程中菜單的那個(gè)例子嗎?(皇上,您還記得大明湖畔的夏雨荷么?)
這次我們用自定義的函數(shù)來重寫1次,會(huì)更詳細(xì)和優(yōu)化:
#include <stdio.h>
int menu()
{
int choice = 0;
while (choice < 1 || choice > 4)
{
printf("菜單 : ");