在數學函數是一種對應關系,C語言里的函數和數學中的函數還是極其相似的
①維基百科:子程序 - 維基百科,自由的百科全書 (shenyinjise.icu)
②子程序。是大型程序中的某部分代碼,由一個或多個語句組成。負責完成某項特定的任務,而且相較于其他代碼具有相對的獨立性。
C語言中的函數,一般會有輸入參數并由返回值,提供對過程的封裝和細節的隱藏,這些代碼通常集成為一個軟件庫。
2、C語言中函數的分類(“庫函數”和“自定義函數”) (1)庫函數①C語言自帶的函數,可以直接使用,方便程序員進行軟件開發
可以在這個網站里面查找: https://cplusplus.com/
或者C/C++官網: cppreference.com(中文版: cppreference.com)
或者使用工具MSDN,可以離線查看,但是更新度不夠
②使用庫函數,需要包含#include對應的頭文件
③常用的庫文件如下:
不可能所有的庫函數都符合我們的需求,因此我們還需要自己寫符合自己需求的函數,所以學會寫自定義函數是最為重要的!
①函數必須有這些構成:函數的返回類型(注意這不是函數的類型)、函數名、函數參數列表、函數體
②偽代碼:函數的結構
返回類型 函數名(參數列表)//注意返回指和參數列表可以是“空”,即void的情況
{函數體(具體代碼);
}//如果忘記寫返回類型,則默認返回一個整數,但不寫返回類型是一個壞習慣!
③一些例子
例子1:比大小的函數
int MAX(int x, int y)//自定義“相加”函數
{return (x>y)?(x):(y);//使用了三目操作符
}
int main()//主體函數
{int num_1 = 10;
int num_2 = 100;
int max = MAX(num_1, num_2);
printf("max = %d\n",max);
return 0;
}
例子2:查找閏年
int is_leap_year(int y)//自定義“判斷是否為閏年”函數
{return ((y % 4 == 0) && (y % 100 != 0)) || y % 400 == 0;
}
int main()//主體函數
{int y = 0;
for (y = 1000; y<= 2000; y++)
{//判斷y是否為閏年
//如果是閏年返回1
//如果不是閏年返回0
if (is_leap_year(y))
{printf("%d ", y);
}
}
return 0;
}
④一般來說要先在主函數(主程序)里面寫如何去用、在哪里用這個函數,再去寫自定義函數的具體實現會比較好一點。(這其實是TDD思想,TDD指“測試驅動開發(Test-Driven Development)”)
3、函數的參數 (1)實際參數(實參)真實傳給函數的參數,實參可以是:常量、變量、表達式、函數等。
實參在進行函數調用的時候,必須有確定的值,然后傳值給形參使用。
①簡單來講就是函數定義后,函數名后面括號的內容中的變量,只有在函數被調用的時候,實參傳值給形參時,形參才會有值(即“才會實例化“,“才會分配內存單元”,因此就是形式上的參數)。
②更簡單理解就是,“形參”是“實參”的臨時拷貝,對形參的改動和修改是不會影響到實參的。
③如果需要通過形參來改動實參,則不能只是傳遞值,而是應該傳遞地址!
④另外形式參數在函數調用完后就自動銷毀了,因此形式參數只有在函數內部才是有效的
⑤函數中的形式參數是在棧中開辟空間的
int founction((a1, a2), (a3, a4), a5, a6);//函數有幾個參數呢?
實際上應該是4個參數,(a1, a2)和(a3, a4)是逗號表達式,他們都各有一個結果,分別為a2和a4,故實際上函數的參數為a2,a4,a5,a6四個參數
(4)函數參數個數設置得越少越好不然用戶使用的時候,還得搞清楚每個參數的意義,并且還要輸入很多參數才能使用
4、函數的調用(使用函數交換兩個數字) (1)傳值調用函數的形參和實參分別占有不同的內存,對形參的修改不會影響實參。
如果寫成下面這個代碼就會交換失?。ū举|就是形參和實參的問題)
//一個典型的例子(寫一個錯誤交換兩個數的函數)
void function(int a, int b)//這里自定義函數只是臨時拷貝(傳過來)num_1和num_2兩個數的值,所以下面只是x和y的值交換了,而num_1和num_2卻沒有進行實際上的交換
{int t = 0;
t = a;
a = b;
b = t;
}
int main()
{int num_1 = 1;
int num_2 = 2;
printf("%d %d\n",num_1, num_2);
function(num_1, num_2);//使用交換函數
printf("%d %d\n", num_1, num_2);
return 0;
}//這個函數通過編譯器的調試功能會更加清晰看到
(2)傳址調用//一個典型的例子(寫一個正確交換兩個數的函數)
void function(int *pa, int *pb)
{int t = 0;
t = *pa;
*pa = *pb;
*pb = t;
}
int main()
{int num_1 = 1;
int num_2 = 2;
printf("%d %d\n", num_1, num_2);
function(&num_1, &num_2);//使用交換函數
printf("%d %d\n", num_1, num_2);
return 0;
}
總之函數的調用無論是傳值還是傳址都有利弊,看情況選擇傳值方式。從功能上傳址調用比較強大,但是容易出現更多問題。
(3)高內聚低耦合函數設計應該追求“高內聚低耦合”:在函數體內實現修改,盡量不要對外部產生影響,否則代碼不便修改
5、函數的“嵌套調用”和“鏈式訪問” (1)嵌套調用(在一個函數里調用了另一個函數)#includevoid function_1()
{printf("aaaaa\n");
}
void function_2()
{int i = 0;
for(i=0; i<3; i++)
{function_1();//調用了另外一個函數
}
}
int main()
{function_2();
return 0;
}
注意:函數可以嵌套調用但是不能嵌套定義(一個A函數里面又定義了一個B函數,但是可以在A定義里面調用一個已經定義好的函數B(或者B定義里面調用一個已經定義好的函數A))
//以下“嵌套定義”是不被允許的!
返回類型 f1(參數列表)
{//一些代碼
返回類型 f2(參數列表)
{//一些代碼
}
//一些代碼
}
(2)鏈式訪問(把一個函數的返回值作為另一個函數的參數)//鏈式訪問例子1
#include#includeint main()
{printf("%d\n", strlen("abcd"));
return 0;
}
//鏈式訪問例子2
#includeint main()
{printf("%d",printf("%d",printf("%d",43)));//printf返回值是讀取到字符的個數
return 0;
}//結果為4321這四個字符,注意不是四千三百二十一,只是單純的輸出4、3、2、1這四個字符
6、函數的聲明和定義//實際上函數的聲明和變量的聲明很像
#includeint a;變量的聲明
int main()
{printf("%d\n",a);//變量的使用
return 0;
}
int a = 10;//變量的定義
//這種寫法也是可以正常打印出來10的
(1)函數的聲明①是為了告訴編譯器存在一個自定義的函數,需要聲明函數的結構
(函數名字、函數參數、返回類型)
②一般在使用函數之前聲明函數
③函數的聲明一般放在頭文件中
交代函數的具體實現方式
(3)教科書里常見函數聲明的使用:#includeint Add(int x, int y);//函數的聲明
int main()
{int a = 1, b = 2;
printf("%d", Add(a, b));//函數的使用
return 0;
}
int Add(int x, int y)//函數的定義
{return x + y;
}
//這種寫法雖然對,但是實際情況一般不會這么寫,函數的聲明一般放在頭文件中,函數的實現則放到另外一個專門用來自定義函數的源文件中
(4)實際上的工程里常見的,函數聲明較多的寫法是://頭文件add.h中
#ifndef __ADD_H__
#define __ADD_H__
int Add(int x, int y);//函數的聲明
#endif
//函數定義源文件add.h
int Add(int x, int y)//函數的定義
{return x + y;
}
//程序主體文件main.h
#include#include "Add.h"
int main()//主函數
{int a = 1,b = 2;
printf("%d", Add(a, b));//函數的使用
return 0;
}
(5)一是適合分工、二是有保密措施(靜態庫.lib的使用)①總不可能一堆程序員擠在一個電腦屏幕上寫代碼吧?多文件使得有程序員得以有效分工,每一個程序員寫各種工作的頭文件和源文件,最后再開始整合比較好
②使用VS產生靜態庫詳細步驟,見我另外一篇博客:VS靜態庫的產生和使用
遞歸適當使用會大大減少代碼量
(1)什么是遞歸函數“直接”或者“間接”調用自己的一種編程技巧,它通常把一件“大事”化成“小事”來進行求解,函數不斷遞推出去,然后再不斷地回歸,組合成“遞歸”一詞。
①最簡單的遞歸
//最簡單的遞歸,main函數自己調用自己
#includeint main()
{printf("abcd\n");
main();
}//不斷打印abcd此時main將不斷消耗棧區的空間,最后棧溢出,程序崩潰
②按照順序打印一個數的每一位數字字符
#includemy_printf(unsigned int x)
{if(x >9)
{my_printf(x / 10);
printf("%u ", x % 10);
}
else
{printf("%d ", x);
}
}
//大事化小
//my_printf(1234)
//my_printf(123)4
//my_printf(12)34
//my_printf(1)234
int main()
{unsigned int number = 0;
scanf("%u",&number);
my_printf(number);
return 0;
}
理解遞歸函數可以像上面一樣大事化小,或者是畫多個圖來理解
③自定義一個函數,實現和strlen函數一樣的功能
#includemy_strlen(const char *str)
{if(*str == '\0')
{return 0;
}
else
{return (1 + my_strlen(str+1));
}
}
int main()
{char arr[10] = "abcdef";
printf("%d", my_strlen(arr));
return 0;
}
④第n個斐波那契數列(不考慮溢出的情況)
#includeint fun(int n)
{if (n<= 2)
return 1;
else
return fun(n - 1) + fun(n - 2);
}
int main()
{int n = 0;
while (scanf("%d", &n) == 1)
{printf("第%d個斐波那契數列為%d\n", n, fun(n));
}
return 0;
}
⑤階乘的實現(不考慮溢出的情況)
#includeint fun(int n)
{if (n<= 1)
{return 1;
}
else
{return n * fun(n - 1);
}
}
int main()
{int n = 0;
scanf("%d", &n);
printf("%d", fun(n));
return 0;
}
(2)遞歸的必要條件①存在限制條件(不能像上面main自己調用自己一樣,否則遲早棧溢出(stack overflow))讓遞歸停下
②每次遞歸都要接近這個條件,不能原封不動
遞歸有可能大量消耗??臻g,計算量極大,有的時候反而效率低,例如:求斐波那契數列的時候求第50個數就會很吃力了,運算時間很久
#includeint function(int n)//用遞歸的方法求斐波那契數列
{int a=1;
int b=1;
int c=1;
while(n >= 3)
{c = a + b;
a = b;
b = c;
n--;
}
return c;
}
int main()
{return 0;
}
#includeint function(int n)
{int a = 1;
while(n >1)
{a *= n;
n--;
}
return a;
}
int main()//利用迭代求n的階乘
{int n = 0;
scanf("%d", &n);
printf("%d", function(n));
return 0;
}
(4)何時選遞歸看情況,那種方便少缺陷就選哪種,沒必要一定要寫出遞歸(當然遞歸能大大減少代碼量)。況且迭代(循環)的方法會更加清晰。
8、函數如何返回多個值(1)使用多個全局變量,在函數內部改變,則相當于函數返回了多個值
(2)返回一個數組指針,就可以通過數組返回多個值
(3)形參使用多個指針變量,在函數體內部解引用,改變指針指向的值
唯獨不能單用return,只能返回一個值,即一個函數只能返回單個結果,不能是多個
你是否還在尋找穩定的海外服務器提供商?創新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧
當前名稱:C語言入門知識——(1)函數基礎-創新互聯
網站路徑:http://vcdvsql.cn/article6/dijhog.html
成都網站建設公司_創新互聯,為您提供關鍵詞優化、外貿網站建設、商城網站、網頁設計公司、網站收錄、網站排名
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯