C語(yǔ)言學(xué)習(xí)之函數(shù)知識(shí)總結(jié)
前言
函數(shù)是C語(yǔ)言的基本單位,學(xué)好函數(shù)有利于程序的模塊化以及避免寫出重復(fù)的代碼從而減少代碼量,并且可以提高程序的復(fù)用性與可讀性。
一、引入
引例:定義6個(gè)整型變量a,b,c,d,e,f,并對(duì)它們?nèi)我赓x值。分別輸出a,b的最大值,c,d的最大值和e,f的最大值
#include <stdio.h> int main() { int a, b, c, d, e, f; a = 5, b = 3, c = 6, d = 87, e = 12, f = 49; if (a > b) printf("%d\n", a); else if (a < b) printf("%d\n", b); if (c > d) printf("%d\n", c); else if (c < d) printf("%d\n", d); if (e > f) printf("%d\n", e); else if (e < f) printf("%d\n", f); return 0; }
通過(guò)觀察可以發(fā)現(xiàn),代碼中有大量重復(fù)的部分,如果我們定義一萬(wàn)個(gè)變量,并兩兩比較求出其中的最大值,那么if...else if...else語(yǔ)句就要寫一萬(wàn)次,這顯然非常累贅。在編程過(guò)程中,經(jīng)常會(huì)發(fā)現(xiàn),雖然數(shù)據(jù)不一樣,但是對(duì)這些數(shù)據(jù)的操作卻是一樣的,例如,引例中求a,b中的最大值與求c,d的最大值所做的操作是一樣的,唯獨(dú)只有被操作的數(shù)據(jù)不同而已。所以如果程序中有大量重復(fù)的作操,但只是針對(duì)的數(shù)據(jù)不一樣時(shí),我們可以通過(guò)定義函數(shù)來(lái)解決代碼重復(fù)的問(wèn)題。上述引例可以通過(guò)如下代碼實(shí)現(xiàn)對(duì)應(yīng)的功能:
#include <stdio.h> void get_max(int i, int j) // 自定義的求最大值的函數(shù) { if (i > j) printf("%d\n", i); else if (i < j) printf("%d\n", j); else printf("Equal!\n"); } int main() { int a, b, c, d, e, f; a = 5, b = 3, c = 6, d = 87, e = 12, f = 49; get_max(a, b); get_max(c, d); get_max(e, f); return 0; }
二、認(rèn)識(shí)函數(shù)
void表示該函數(shù)沒(méi)有返回值,get_max是該函數(shù)的名字,函數(shù)名后括號(hào)中的變量i和j被稱為該函數(shù)的形式參數(shù)(簡(jiǎn)稱形參)。
void get_max(int i, int j) { if (i > j) printf("%d\n", i); else if (i < j) printf("%d\n", j); }
所有的語(yǔ)言程序的入口都是main函數(shù),從main函數(shù)進(jìn)入則開始順序逐行執(zhí)行main函數(shù)中的代碼。下面代碼塊中定義完變量并完成賦值后,程序執(zhí)行到get_max(a, b);,此時(shí)程序便會(huì)從main函數(shù)的上方查找一個(gè)名叫g(shù)et_max函數(shù)并將括號(hào)中的變量a和b的值分別傳輸?shù)絞et_max函數(shù)名后括號(hào)中的形參i和j中,然后程序會(huì)跳轉(zhuǎn)到get_max函數(shù)的內(nèi)部執(zhí)行,待get_max函數(shù)執(zhí)行完畢后,再跳轉(zhuǎn)回main函數(shù)繼續(xù)執(zhí)行main函數(shù)中的下一條語(yǔ)句。
int main() { int a, b, c, d, e, f; a = 5, b = 3, c = 6, d = 87, e = 12, f = 49; get_max(a, b); get_max(c, d); get_max(e, f); return 0; }
三、函數(shù)的作用
函數(shù)是一種工具,它是能夠完成特定功能的獨(dú)立代碼塊,它不是為某個(gè)特定的問(wèn)題設(shè)計(jì)的,而是為解決大量同類型的問(wèn)題設(shè)計(jì)的;雖然函數(shù)處理的數(shù)據(jù)是不同的,但是對(duì)這些數(shù)據(jù)的操作是相同的;函數(shù)的使用可以避免寫大量重復(fù)的代碼,減少程序員的編碼量;同時(shí)函數(shù)的存在有利于整個(gè)程序的模塊化,函數(shù)可以將復(fù)雜的問(wèn)題剖解為一個(gè)個(gè)簡(jiǎn)單的問(wèn)題;函數(shù)可以被視為一個(gè)“黑匣子”,我們只需要知道某個(gè)函數(shù)的用法即可使用該函數(shù),但我們并不一定知道該函數(shù)是怎么實(shí)現(xiàn)的,例如,我們雖然經(jīng)常使用的printf函數(shù),但我們并不知道其中到底是如何實(shí)現(xiàn)輸出功能的。同樣的,當(dāng)我們編寫函數(shù)的時(shí)候,也可以把具體實(shí)現(xiàn)的過(guò)程隱藏起來(lái),因?yàn)檫@可能是商業(yè)機(jī)密。
四、函數(shù)的返回值
函數(shù)不僅可以接收數(shù)據(jù),并對(duì)接收到的數(shù)據(jù)進(jìn)行處理,而且也可以將數(shù)據(jù)處理的結(jié)果返回,例如下述程序:
#include <stdio.h> int f(void) // int表示函數(shù)的返回值是int類型的值;括號(hào)中的void表示該函數(shù)不能接收任何數(shù)據(jù) { return 10; // return語(yǔ)句表示向主調(diào)函數(shù)返回一個(gè)值 } int main() { int i = 88; i = f(); // 調(diào)用f函數(shù)后,f函數(shù)就會(huì)返回10這個(gè)數(shù)據(jù),所以這行代碼就相當(dāng)于把10賦值給變量i printf("%d\n", i); return 0; }
思考:下述程序是否正確?
#include <stdio.h> void g(void) // 函數(shù)名前的void表示該函數(shù)沒(méi)有返回值 { } void h(void) { return 10; // 編譯時(shí)如果沒(méi)有調(diào)用該函數(shù),則不會(huì)報(bào)錯(cuò),一旦調(diào)用便會(huì)報(bào)錯(cuò),因?yàn)閔函數(shù)的返回值為空 } int main() { int i = 88; i = g(); // 由于g函數(shù)沒(méi)有返回值,所以不能將其賦值給變量i,編譯時(shí)會(huì)報(bào)錯(cuò) printf("%d\n", i); return 0; }
五、定義函數(shù)
六、函數(shù)的類型
函數(shù)返回值的類型也稱為函數(shù)的類型,如果函數(shù)名前的返回值類型與函數(shù)執(zhí)行體中的return表達(dá)式的返回的類型不同,則最終函數(shù)返回值以函數(shù)名前的返回值類型為準(zhǔn)。
思考:下述程序輸出的結(jié)果是多少?
#include <stdio.h> int f() // 如果函數(shù)名后的括號(hào)里是空的,等同于在括號(hào)里寫void { return 10.5; } int main() { double x = 6.6; x = f(); printf("%lf\n", x); return 0; }
通過(guò)程序可得,變量x輸出的值為10.000000,這就說(shuō)明,函數(shù)最終的返回值與函數(shù)名前的返回值類型相同,而不是以return表達(dá)式為準(zhǔn)。所以,函數(shù)的類型取決于函數(shù)名前的返回值的類型。
七、return語(yǔ)句與break語(yǔ)句的區(qū)別
break語(yǔ)句的作用是終止當(dāng)前循環(huán)和switch語(yǔ)句,而return語(yǔ)句與break語(yǔ)句有本質(zhì)性的差別,return語(yǔ)句的作用是終止當(dāng)前正在執(zhí)行的函數(shù)。當(dāng)被調(diào)函數(shù)的返回值為空時(shí),則直接終止被調(diào)函數(shù)然后跳回主調(diào)函數(shù)繼續(xù)順序執(zhí)行主調(diào)函數(shù)中的代碼;如果被調(diào)函數(shù)的返回值不為空時(shí),則先將返回值返回給主調(diào)函數(shù),然后再終止被調(diào)函數(shù)并跳回主調(diào)函數(shù)繼續(xù)順序執(zhí)行主調(diào)函數(shù)中的代碼。
思考:以下兩個(gè)程序的輸出結(jié)果是否相同?如果不同,這兩個(gè)程序的輸出結(jié)果分別是什么?
#incldude <stdio.h> void f() { for (i = 0; i < 5; i++) { printf("AAAA\n"); break; } printf("BBBB\n"); } int main() { f(); return 0; }
#incldude <stdio.h> void f() { for (i = 0; i < 5; i++) { printf("AAAA\n"); return; } printf("BBBB\n"); } int main() { f(); return 0; }
八、函數(shù)的分類
根據(jù)是否有形參可以將函數(shù)分為,有參函數(shù)和無(wú)參函數(shù);根據(jù)是否有返回值可以將函數(shù)分為有返回值函數(shù)和無(wú)返回值函數(shù);同時(shí),也可以將函數(shù)分為庫(kù)函數(shù)和自定義函數(shù),例如,printf函數(shù)就屬于庫(kù)函數(shù),因?yàn)槭窍到y(tǒng)提供給我們的,而這篇文章中的get_max函數(shù),f函數(shù),g函數(shù)等均屬于自定義函數(shù)。
九、主函數(shù)
不管一個(gè)程序中有多少個(gè)函數(shù),但主函數(shù)(也就是main函數(shù))只能有一個(gè),并且main函數(shù)是整個(gè)程序的入口,也是整個(gè)程序的出口,主函數(shù)可以調(diào)用任何其他函數(shù),其他函數(shù)之間也可以互相調(diào)用,但是其他函數(shù)不能調(diào)用主函數(shù)。
十、函數(shù)使用舉例
定義6個(gè)整型變量a,b,c,d,e,f,并對(duì)它們?nèi)我赓x值。分別輸出a,b的最大值,c,d的最大值和e,f的最大值
要求:自定義函數(shù)并且不能與引例中的函數(shù)執(zhí)行體相同
#include <stdio.h> int get_max(int i, int j) { if (i > j) return i; else if (i < j) return j; } int main() { int a, b, c, d, e, f; a = 5, b = 3, c = 6, d = 87, e = 12, f = 49; printf("%d\n", get_max(a, b)); printf("%d\n", get_max(c, d)); printf("%d\n", get_max(e, f)); return 0; }
輸入三個(gè)int類型的數(shù)字,并判斷這三個(gè)數(shù)字是否是素?cái)?shù)。如果是素?cái)?shù)輸出Yes,如果不是輸出No
要求:使用自定義函數(shù)實(shí)現(xiàn)
提示:素?cái)?shù)(又稱質(zhì)數(shù))是大于1的自然數(shù)并且素?cái)?shù)只能被1和它本身整除
#include <stdio.h> int is_prime(int val) { int i; for (i = 2; i < val; i++) if (val % i == 0) break; if (i == val) return 1; else return 0; } int main() { int val1, val2, val3, i; scanf("%d%d%d", &val1, &val2, &val3); if ( is_prime(val1) ) printf("Yes!\n"); else printf("No!\n"); if ( is_prime(val2) ) printf("Yes!\n"); else printf("No!\n"); if ( is_prime(val3) ) printf("Yes!\n"); else printf("No!\n"); return 0; }
十一、函數(shù)的聲明
觀察可以發(fā)現(xiàn),文章中所有自定義的函數(shù)都放在了main函數(shù)之上,那我們?nèi)绻麑⒆远x的函數(shù)放在main函數(shù)的下面會(huì)出現(xiàn)什么情況呢?如果我們?cè)趍ain函數(shù)中調(diào)用了自定義函數(shù),并且將自定義的函數(shù)放在了main函數(shù)的下方,此時(shí)整個(gè)程序就會(huì)編譯報(bào)錯(cuò),這是因?yàn)閙ain函數(shù)執(zhí)行到調(diào)用自定義函數(shù)的語(yǔ)句時(shí),它只會(huì)向上查找對(duì)應(yīng)的函數(shù),而如果在它上面沒(méi)有對(duì)應(yīng)的函數(shù),程序就會(huì)出錯(cuò)。
思考1:下面兩個(gè)程序是否可以運(yùn)行?
#include <stdio.h> int main() { f(); return 0; } void f() { printf("AAAA\n"); }
那如果我們要把被調(diào)函數(shù)放在主調(diào)函數(shù)之后,我們應(yīng)該怎么辦呢?這需要在主調(diào)函數(shù)的上方加上函數(shù)聲明即可,如下程序所示:
#include <stdio.h> void f(); // 這是函數(shù)聲明,后面的分號(hào)不可以省略 int main() { f(); return 0; } void f() { printf("AAAA\n"); }
思考2:下面的程序是否可以運(yùn)行?如果有錯(cuò)誤應(yīng)該如何改正?
#include <stdio.h> void g(void) { f(); } void f(void) { printf("AAAA\n"); } int main() { g(); return 0; }
十二、函數(shù)的形參與實(shí)參
形參是指定義函數(shù)時(shí)括號(hào)中定義的變量,而實(shí)參是指在調(diào)用函數(shù)時(shí)向被調(diào)函數(shù)傳輸?shù)木唧w的數(shù)據(jù)或變量。如下代碼所示,變量i就是形參,而主函數(shù)中調(diào)用f函數(shù)時(shí)在后面的括號(hào)中寫的5就是實(shí)參。需要注意的是,形參和實(shí)參的個(gè)數(shù)必須是對(duì)應(yīng)的,數(shù)據(jù)類型也必須相互兼容。
#include <stdio.h> void f(int i) { printf("%d\n", i); } int main() { f(5); return 0; }
十三、合理設(shè)計(jì)函數(shù)
在掌握了以上知識(shí)之后,我們?cè)撊绾卧O(shè)計(jì)函數(shù)讓整個(gè)程序更像是開發(fā)軟件?答案很簡(jiǎn)單,如果僅需要使用一次某個(gè)功能,則不需要將該功能設(shè)計(jì)成一個(gè)函數(shù),但如果需要多次使用該功能,則設(shè)計(jì)一個(gè)該功能對(duì)應(yīng)的函數(shù)是不二選擇。這段話通過(guò)以下三個(gè)程序體會(huì):
示例:輸入一個(gè)int類型的數(shù)字n,求1到n之間(包括n)所有的素?cái)?shù)并輸出。不考慮輸入的值小于等于1的情況
解法1:不定義其它函數(shù),僅在main函數(shù)中實(shí)現(xiàn)
#include <stdio.h> int main() { int n; int i, j; scanf("%d", &n); for (i = 2; i <= n; i++) { for (j = 2; j < i; j++) if (i % j == 0) break; if (j == i) printf("%d\n", i); } return 0; }
這樣寫程序雖然實(shí)現(xiàn)了功能,但代碼的重用性非常低,比如,我們要分別判斷100個(gè)不同數(shù)字從1到它本身之間的所有素?cái)?shù),則需要將下面的代碼塊重復(fù)寫100次,這樣就會(huì)導(dǎo)致代碼大部分是重復(fù)的,而且代碼量會(huì)顯得非常大。
for (i = 2; i <= n; i++) { for (j = 2; j < i; j++) if (i % j == 0) break; if (j == i) printf("%d\n", i); }
考慮到要將1到n之間所有的數(shù)字都進(jìn)行判斷素?cái)?shù)這一操作,所以可以單獨(dú)設(shè)計(jì)出一個(gè)函數(shù)實(shí)現(xiàn)判斷素?cái)?shù)這個(gè)功能,從而解決代碼重復(fù)的問(wèn)題。
解法2:自定義一個(gè)函數(shù)實(shí)現(xiàn)
#include <stdio.h> int is_prime(int val) { int i; for (i = 2; i < val; i++) if (val % i == 0) break; if (i == val) return 1; else return 0; } int main() { int n; int i; scanf("%d", &n); for (i = 2; i <= n; i++) if ( is_prime(i) ) printf("%d\n", i); return 0; }
相比較第一個(gè)程序,該程序更容易讓人理解,代碼的可重用性也得以提高,并且多次判斷時(shí)所需要寫的代碼也相對(duì)較少,但存在的問(wèn)題與第一個(gè)程序相同,如果要分別判斷100個(gè)不同數(shù)字從1到它本身之間的所有素?cái)?shù),則需要將下面的代碼塊重復(fù)寫100次。
for (i = 2; i <= n; i++) if ( is_prime(i) ) printf("%d\n", i);
又考慮到判斷完是否是素?cái)?shù)后,輸出也是重復(fù)性操作,所以同樣可以將輸出這一操作通過(guò)設(shè)計(jì)函數(shù)來(lái)實(shí)現(xiàn)。
解法3:自定義兩個(gè)函數(shù)實(shí)現(xiàn)
#include <stdio.h> // 本函數(shù)的功能是判斷val是否是素?cái)?shù) int is_prime(int val) { int i; for (i = 2; i < val; i++) if (val % i == 0) break; if (i == val) return 1; else return 0; } // 本函數(shù)的功能是將1到n之間(包括n)所有的素?cái)?shù)輸出 void traverse(int n) { int i; for (i = 2; i <= n; i++) if ( is_prime(i) ) printf("%d\n", i); } int main() { int n; int i; scanf("%d", &n); traverse(n); return 0; }
第三個(gè)程序相較前兩個(gè)程序而言,代碼量更少,且可重用性更高,如果需要判斷多個(gè)數(shù)字并輸出的話,只需要多次調(diào)用traverse函數(shù)即可實(shí)現(xiàn)功能,而且整個(gè)程序更容易讓人理解。
總結(jié):一個(gè)函數(shù)的功能盡可能獨(dú)立單一,不要將多個(gè)功能放在一個(gè)函數(shù)中實(shí)現(xiàn),這樣不僅可以避免代碼的重復(fù)而且還可以增加整個(gè)程序的可重用性與可讀性。
十四、變量的作用域
變量按作用域劃分可以分為全局變量和局部變量。
全局變量
在所有函數(shù)外部定義的變量被稱為全局變量。如下程序所示,變量k就是一個(gè)全局變量,需要注意的是,全局變量只能在定義全局變量的位置之后的函數(shù)中使用,在全局變量之上的函數(shù)不能使用。
#include <stdio.h> int k = 10; void f() { printf("%d\n", k); } int main() { f(); return 0; }
局部變量
在函數(shù)內(nèi)部定義的變量,以及函數(shù)的形參都屬于局部變量。如下程序所示,f函數(shù)中的形參i以及f函數(shù)中定義的變量j都是f函數(shù)的局部變量,main函數(shù)中定義的變量i也是main函數(shù)的局部變量。除此之外,初學(xué)時(shí)可能會(huì)有這樣的困惑:f函數(shù)中定義了i變量,main函數(shù)中也定義了i變量,這樣程序是否存在問(wèn)題?答案是不存在問(wèn)題的,因?yàn)閙ain函數(shù)中變量i只在main函數(shù)中發(fā)揮作用,而f函數(shù)中的變量i只在f函數(shù)中發(fā)揮作用,所以它們并不會(huì)起沖突,也就意味著,所有的局部變量只能在函數(shù)的內(nèi)部使用,這也正是局部變量的含義。
#include <stdio.h> void f(int i) { int j = 20; } int main() { int i = 10; return 0; }
全局變量名與局部變量名沖突的問(wèn)題
#include <stdio.h> int i = 99; void f(int i) { printf("i = %d\n", i); } int main() { f(8); return 0; }
推測(cè)1:程序中有錯(cuò)誤,因?yàn)槿肿兞棵c局部變量名一樣,無(wú)法分辨f函數(shù)中到底是全局變量i還是局部變量i
推測(cè)2:程序無(wú)誤,如果輸出結(jié)果為99,則此時(shí)的變量i是全局變量;如果輸出結(jié)果為8,則此時(shí)的變量i是局部變量
結(jié)果:輸出結(jié)果為8
結(jié)論:如果全局變量名與局部變量名相同,則局部變量會(huì)屏蔽全局變量的存在
十五、函數(shù)內(nèi)存的分配
在使用函數(shù)之前,操作系統(tǒng)會(huì)給函數(shù)中所有的變量分配內(nèi)存空間,但當(dāng)函數(shù)被執(zhí)行完畢后,其中所有變量的內(nèi)存均會(huì)被釋放掉,等到再次使用時(shí),會(huì)重新給函數(shù)中的變量分配內(nèi)存空間。大家可以思考一下這樣做的合理性。
以上就是C語(yǔ)言學(xué)習(xí)之函數(shù)知識(shí)總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于C語(yǔ)言 函數(shù) 的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)中串的模式匹配
這篇文章主要介紹了C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)中串的模式匹配的相關(guān)資料,需要的朋友可以參考下2017-05-05詳解C++設(shè)計(jì)模式編程中對(duì)狀態(tài)模式的運(yùn)用
這篇文章主要介紹了C++設(shè)計(jì)模式編程中對(duì)狀態(tài)模式的運(yùn)用,狀態(tài)模式允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為,對(duì)象看起來(lái)似乎修改了它的類,需要的朋友可以參考下2016-03-03C++ boost::asio編程-域名解析詳細(xì)介紹
這篇文章主要介紹了C++ boost::asio編程-域名解析詳細(xì)介紹的相關(guān)資料,這里附有實(shí)例代碼,幫助大家學(xué)習(xí)理解這部分知識(shí),需要的朋友可以參考下2016-11-11C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的推箱子游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的推箱子游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01