姓名:王懷帥? 學(xué)號(hào):16040410035
轉(zhuǎn)載自:http://www.itdecent.cn/p/89c81a34a83a=有修改
【嵌牛導(dǎo)讀】:C語(yǔ)言中函數(shù)對(duì)各類問(wèn)題的實(shí)際性解決方案
【嵌牛鼻子】:C語(yǔ)言函數(shù)
【嵌牛提問(wèn)】:如何理解函數(shù)在C語(yǔ)言中的作用以及如何使用這些函數(shù)?
【嵌牛正文】:
函數(shù)的由來(lái)
程序=數(shù)據(jù)+算法
C程序=數(shù)據(jù)+函數(shù)
模塊化程序設(shè)計(jì)

模塊化程序設(shè)計(jì).png
面向過(guò)程的程序設(shè)計(jì)
以過(guò)程為中心的編程思想
首先將復(fù)雜的問(wèn)題,分解為一個(gè)個(gè)容易的問(wèn)題
分解過(guò)后的問(wèn)題可以按照步驟一步步完成
函數(shù)是C語(yǔ)言面向過(guò)程的一種體現(xiàn)
解決問(wèn)題的每個(gè)步驟可以用函數(shù)來(lái)實(shí)現(xiàn)
聲明&&定義
聲明就是預(yù)先告訴編譯器實(shí)體的存在
定義就是明確指示編譯器實(shí)體的意義
#include
extern int g_var;//全局的
void f(int i, int j);//函數(shù)聲明
int main()
{
//extern int g_var;//局部變量聲明
int g(int x);//函數(shù)聲明
g_var = 10;
f(1, 2);
printf("%d\n", g(3));
return 0;
}
void f(int i, int j)//函數(shù)定義
{
printf("i + j = %d\n", i + j);
}
int g(int x)//函數(shù)定義
{
return 2 * x + g_var;
}
文件二
int g_var = 0;
函數(shù)的參數(shù)
函數(shù)的參數(shù)在本質(zhì)上與局部變量相同,都是在棧上面分配空間
函數(shù)參數(shù)的初始值是函數(shù)調(diào)用時(shí)的實(shí)參值
#include
int f(int i, int j)
{
printf("%d, %d\n", i, j);
}
int main()
{
int k = 1;
f(k, k++);
printf("%d\n", k);
return 0;
}
這里的輸出是2,1,2
解釋
函數(shù)的參數(shù)的求值順序是依賴于編譯器的實(shí)現(xiàn)!
在程序到達(dá)順序點(diǎn)的時(shí)候,之前所做的一切操作必須反映到后續(xù)訪問(wèn)中.
也就是說(shuō)在這個(gè)例子中,在gcc編譯器下面,先傳第二個(gè)參數(shù)到函數(shù),然后,再進(jìn)行++運(yùn)算,在進(jìn)行第一個(gè)參數(shù)傳遞
.
可變參數(shù)列表
可變參數(shù)的含義
C語(yǔ)言中可以定義參數(shù)可變的函數(shù)
參數(shù)可變的函數(shù)實(shí)現(xiàn)依賴于stdarg.h頭文件
va_list變量va_start(),va_end和va_arg配合使用能夠訪問(wèn)參數(shù)值
在C語(yǔ)言中是沒(méi)有重載,但是可以使用可變參數(shù)列表
#include
#include
float average(int n, ...)
{
va_list args;
int i = 0;
float sum = 0;
va_start(args, n);
for(i=0; i
{
sum += va_arg(args, int);
}
va_end(args);
return sum / n;
}
int main()
{
printf("%f\n", average(5, 1, 2, 3, 4, 5));
printf("%f\n", average(4, 1, 2, 3, 4));
return 0;
}
可變參數(shù)的限制
可變參數(shù)必須從頭到尾按照順序逐個(gè)訪問(wèn)
參數(shù)列表至少一個(gè)確定命名的參數(shù)
可變參數(shù)宏是沒(méi)有辦法判斷實(shí)際存在的參數(shù)的數(shù)量
可變參數(shù)宏無(wú)法判斷參數(shù)的實(shí)際類型
小結(jié)
可變參數(shù)是C語(yǔ)言提供的一種函數(shù)設(shè)計(jì)的技巧
可變參數(shù)提供了一種更方便的函數(shù)調(diào)用方式
可變參數(shù)必須順序的訪問(wèn)
無(wú)法直接訪問(wèn)可變參數(shù)列表中間的參數(shù)值
函數(shù)調(diào)用行為
活動(dòng)記錄
活動(dòng)記錄是函數(shù)調(diào)用時(shí)用于記錄一系列相關(guān)信息的記錄
臨時(shí)變量域:用來(lái)存放臨時(shí)變量的值,如k++的中間結(jié)果
局部變量域:用來(lái)存放函數(shù)本次執(zhí)行中的局部變量
機(jī)器狀態(tài)域:用來(lái)保存調(diào)用函數(shù)之前有關(guān)機(jī)器狀態(tài)的信息,包括各種寄存器的當(dāng)前值和返回地址等。(棧頂指針等)
實(shí)參域:用來(lái)存放函數(shù)實(shí)參信息
返回值域:為調(diào)用者函數(shù)存放返回值
函數(shù)參數(shù)計(jì)算的次序是依賴編譯器實(shí)現(xiàn)的,參數(shù)的入棧次序如何確定?
調(diào)用約定(在調(diào)用動(dòng)態(tài)連接庫(kù)的時(shí)候可以用)
當(dāng)函數(shù)被調(diào)用時(shí),參數(shù)會(huì)傳遞給被調(diào)用的函數(shù),函數(shù)調(diào)用約定就是描述是如何傳遞到??臻g的,以及??臻g由誰(shuí)維護(hù)
參數(shù)的傳遞順序
從右到左依次入棧: __stdcall,__cdecl,__thiscall
從左到右依次入棧:__pascal,__fastall
調(diào)用堆棧清理
調(diào)用者清除棧
被調(diào)用函數(shù)返回后清除棧
小結(jié)
函數(shù)調(diào)用時(shí)C語(yǔ)言的核心機(jī)制
活動(dòng)記錄保存了函數(shù)調(diào)用以及返回所需要的一切信息
調(diào)用約定是調(diào)用者和開(kāi)發(fā)者之間的調(diào)用協(xié)議,常用于不同的開(kāi)發(fā)者編寫的庫(kù)函數(shù)調(diào)用
函數(shù)的設(shè)計(jì)技巧
不要再函數(shù)中使用全局變量,盡量讓函數(shù)從意義上是一個(gè)獨(dú)立功能模塊
參數(shù)名要能夠體現(xiàn)參數(shù)意義
如果說(shuō)傳遞的參數(shù)為指針,且僅僅作輸入?yún)?shù)用,則應(yīng)在類型前加const,以防止該指針在函數(shù)體內(nèi)被惡意修改
不要省略返回值的類型,如果函數(shù)沒(méi)有返回值,那么應(yīng)當(dāng)聲明為void
在函數(shù)體的“入口處”,對(duì)參數(shù)的有效性進(jìn)行檢查,對(duì)指針的檢查尤為重要
語(yǔ)句不可返回指向“棧內(nèi)存”的“指針”,因?yàn)樵搩?nèi)存會(huì)在函數(shù)結(jié)束后銷毀
函數(shù)體的規(guī)模要小
相同的輸入應(yīng)當(dāng)產(chǎn)生相同的輸出,盡量避免函數(shù)帶有“記憶”功能少用static
避免函數(shù)有太多的參數(shù),參數(shù)個(gè)數(shù)應(yīng)當(dāng)控制在4個(gè)以內(nèi)
有時(shí)候函數(shù)不需要返回值,但是增加靈活性,可以附加返回值
函數(shù)名與返回值在語(yǔ)義上不能沖突