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

