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