函數(shù)
函數(shù)的分類
在C語言中可從不同的角度對函數(shù)分類
-
從函數(shù)定義的角度看,函數(shù)可分為庫函數(shù)和用戶定義函數(shù)兩種
- 庫函數(shù): 由C語言系統(tǒng)提供,用戶無須定義
- 用戶定義函數(shù):由用戶按需編寫的函數(shù)。
-
從函數(shù)執(zhí)行結(jié)果的角度來看, 函數(shù)可分為有返回值函數(shù)和無返回值函數(shù)兩種
- 有返回值函數(shù): 此類函數(shù)被調(diào)用執(zhí)行完后將向調(diào)用者返回一個執(zhí)行結(jié)果,稱為函數(shù)返回值唆垃。(必須指定返回值類型和使用return關鍵字返回對應數(shù)據(jù))
- 無返回值函數(shù): 此類函數(shù)用于完成某項特定的處理任務,執(zhí)行完成后不向調(diào)用者返回函數(shù)值五芝。(返回值類型為void, 不用使用return關鍵字返回對應數(shù)據(jù))
-
從主調(diào)函數(shù)和被調(diào)函數(shù)之間數(shù)據(jù)傳送的角度看,又可分為無參函數(shù)和有參函數(shù)兩種
- 無參函數(shù): 在函數(shù)定義及函數(shù)說明及函數(shù)調(diào)用中均不帶參數(shù)。主調(diào)函數(shù)和被調(diào)函數(shù)之間不進行參數(shù)傳送辕万。
- 有參函數(shù): 在函數(shù)定義及函數(shù)說明時都有參數(shù),稱為形式參數(shù)(簡稱為形參)枢步。在函數(shù)調(diào)用時也必須給出參數(shù),稱為實際參數(shù)(簡稱為實參)
-
為什么要定義函數(shù)?
- 首先,如果不定義函數(shù):
- 重復代碼冗余代碼過多;
- 需求發(fā)生改變時;需要修改很多代碼
- 如果定義函數(shù)
- 將某些代碼封裝起來,方便以后的使用;
- 提高代碼的復用性;
- 使代碼更加簡潔;
- 便于維護;
- 首先,如果不定義函數(shù):
函數(shù)的定義
- 格式:
// 標注 有參數(shù)有返回值函數(shù)的定義
返回值類型 函數(shù)名稱(形參列表){
被封裝的函數(shù);
返回值;
}
例子:
int main(){
printf("hello world");
return 0;
}
// 獲取最大值
int getMax(int num1, int num2){
int max = num1 > num2 ? num1 : num2;
return max;
}
// 無參數(shù)無返回值的函數(shù)定義:
void 函數(shù)名(){
函數(shù)體;
}
// 無參有返回值函數(shù)定義
返回值類型 函數(shù)名(){
函數(shù)體;
return 值;
}
// 有參無返回值函數(shù)定義
void 函數(shù)名(參數(shù)類型 形式參數(shù)1, 參數(shù)類型 形式參數(shù)2, ...){
函數(shù)體;
}
- 初學者如何定義函數(shù)
- 1.確定函數(shù)的名稱(給函數(shù)起一個有意義的名稱,讓調(diào)用者見名知意)
- 注意點: 函數(shù)名稱也是標識符的一種,所以也需要遵守標識符的命名規(guī)則: 字母,數(shù)字,下劃線的駝峰命名;
- 2.確定形參列表(告訴調(diào)用者,調(diào)用時是否需要傳遞一些輔助的數(shù)據(jù));
- 注意點: 形參列表的格式 (數(shù)據(jù)類型 變量名稱, 數(shù)據(jù)類型 變量名稱, ...)
- 數(shù)據(jù)類型 變量名稱 可以有零個或多個;
- 3.確定函數(shù)的功能代碼(也就是要封裝的代碼)
- 4.確定返回值和返回值的類型
- return 的作用之一就是將后面的數(shù)據(jù)返回給函數(shù)的調(diào)用者;
#include <stdio.h>
int getAverage(int num1, int num2){
int avg = (num1 + num2) / 2;
return avg;
}
int main()
{
int a = 10;
int b = 20;
// int res = (a + b) / 2;
int res = getAverage(a, b);
printf("average = %i\n", res);
return 0;
}
// 注意點: main函數(shù)的作用域和getAverage作用域不同
函數(shù)的參數(shù)和返回值
- 函數(shù)的參數(shù):
- 函數(shù)可以有參數(shù),也可以沒有參數(shù);
- 函數(shù)的參數(shù)可以是零個或多個;
- 函數(shù)可以有返回值;也可以沒有返回值;
- 注意:如果函數(shù)沒有返回值,那么返回值類型寫void就行;
- 函數(shù)的返回值類型可以是所有C語言類型
- 形式參數(shù):
- 在定義函數(shù)時,函數(shù)名后面小括號()中定義的變量成為形式參數(shù),簡稱形參
- 形參變量只有在被調(diào)用時才分配內(nèi)存單元,在調(diào)用結(jié)束時,即刻釋放所分配的內(nèi)存單元。
- 因此,形參只有在函數(shù)內(nèi)部有效,函數(shù)調(diào)用結(jié)束返回主調(diào)函數(shù)后則不能再使用該形參變量
int getSum(int num1, int num2){ // 形式參數(shù)
return num1+num2;
}
- 實際參數(shù):
- 在調(diào)用函數(shù)時,傳入的值稱為實際參數(shù),簡稱實參;
- 實參可以是常量渐尿、變量醉途、表達式、函數(shù)等,無論實參是何種類型的量,在進行函數(shù)調(diào)用時,它們都必須具有確定的值,以便把這些值傳送給形參
- 因此應預先用賦值,輸入等辦法使實參獲得確定值
int main(){
int a = 99, b =100;
int sum = getSum(a, b); // 實際參數(shù)
}
- 返回值的注意點:
- 如果函數(shù)沒有編寫返回值類型,那么默認就是int類型!
- 如果函數(shù)的返回值類型和實際return返回的類型不同,那么會隱式(自動)轉(zhuǎn)換為返回值類型;
test1(){
return 3.14;
}
// 返回值為3
//如果函數(shù)的返回值類型和實際return返回的類型不同,那么會隱式(自動)轉(zhuǎn)換為返回值類型
int test2(){
return 3.14; // double
}
// 返回值3
int main()
{
double res = test1();
printf("res = %lf",res); // 3.000000
return 0;
}
- 函數(shù)的注意點:
-
1.函數(shù)的名稱不能相同;
- 哪怕返回類型不同,函數(shù)名稱也不能相同
- 哪怕形參列表不同,函數(shù)名臣也不能相同
- 2.函數(shù)不能嵌套定義,哪怕編譯器允許的,也不建議這么干;
- 3.如果是基本類型的數(shù)據(jù)作為函數(shù)的參數(shù) ,那么在函數(shù)內(nèi)修改形參, 不會影響外面實參的值;
- 4.在函數(shù)的內(nèi)部不能定義和形參同名的變量
void test(){ int num = 666; // 這么定義變量會報錯 printf("num = %i\n", num); }
- 函數(shù)不要嵌套定義
-
1.函數(shù)的名稱不能相同;
函數(shù)的聲明
函數(shù)的定義可以寫在函數(shù)調(diào)用之前,也可以寫在函數(shù)調(diào)用之后,但是要進行函數(shù)的聲明;
標準的寫法:函數(shù)的聲明只需要將函數(shù)定義{前面的代碼拷貝即可;
函數(shù)聲明,就是在函數(shù)調(diào)用之前告訴系統(tǒng), 該函數(shù)叫什么名稱, 該函數(shù)接收幾個參數(shù), 該函數(shù)的返回值類型是什么
函數(shù)的聲明只需將函數(shù)定義{前面的代碼拷貝即可
-
聲明函數(shù)的注意點:
-
1.函數(shù)的聲明只要寫在調(diào)用之前
- 也就是說,函數(shù)的聲明,可以寫在函數(shù)的外面
- 也可以寫在函數(shù)里面
2.函數(shù)聲明的時候,形參可以不用在指定名稱
3.如果函數(shù)的返回值類型是整型,那么可以不用編寫函數(shù)聲明(但是還是建議寫上);
由于函數(shù)聲明僅僅是為了告訴編譯器,我們有一個什么函數(shù),所以函數(shù)的聲明是可以聲明多次,但是函數(shù)的的實現(xiàn)只能一次;
-
總結(jié):
- 1.以后但凡函數(shù)的定義寫在函數(shù)調(diào)用之前,一定要在調(diào)用之前編寫函數(shù)的聲明;
- 2.函數(shù)的聲明只會告訴系統(tǒng)函數(shù)叫什么,接收幾個什么類型參數(shù),返回值是什么類型即可;
- 3.一般情況下我們會將函數(shù)的聲明寫到函數(shù)的外面,而不會寫到函數(shù)的里面;
-
main函數(shù)分析
-
main函數(shù)分析
- main是函數(shù)的名稱, 和我們自定義的函數(shù)名稱一樣, 也是一個標識符
- 只不過main這個名稱比較特殊, 程序已啟動就會自動調(diào)用它
-
return 0;的含義:
- 告訴系統(tǒng)main函數(shù)是否正確的被執(zhí)行了
- 如果main函數(shù)的執(zhí)行正常, 那么就返回0
- 如果main函數(shù)執(zhí)行不正常, 那么就返回一個非0的數(shù)
-
返回值類型:
- 一個函數(shù)return后面寫的是什么類型, 函數(shù)的返回值類型就必須是什么類型, 所以寫int
在企業(yè)開發(fā)中,我們是可以通過控制臺(cmd)在運行程序的時候給main函數(shù)傳遞參數(shù)就可以了
int main(int argc, const char *argv[])
{
// 代表數(shù)組中有一個元素
// 代表數(shù)組中有兩個元素
// 默認情況下, 系統(tǒng)在調(diào)用main函數(shù)的時候, 會給argv這個數(shù)組中存放一個元素
printf("argc = %i\n", argc);
printf("argv[0] = %s\n", argv[0]);
printf("argv[1] = %s\n", argv[1]);
printf("argv[2] = %s\n", argv[2]);
return 0;
}
遞歸函數(shù)
- 遞歸: 自己搞自己
- 遞歸可以實現(xiàn)循環(huán)的功能,但是遞歸的性能比循環(huán)差很多
- return 只能返回給調(diào)用者
// do - while 寫法:
int num = -1;
do{
// 提醒用戶如何輸入數(shù)據(jù)
printf("請輸入一個正整數(shù),以回車結(jié)束;\n");
// 接收用戶輸入的數(shù)據(jù);
scanf("%i", &num);
}while( num < 0);
// 輸出
printf("%i",num);
// 遞歸寫法
int test(){
// 定義用戶保存的變量
int num = -1;
// 提示用戶如何輸入數(shù)據(jù)
printf("請輸入一個正整數(shù),以回車結(jié)束;\n");
// 接收用戶輸入的數(shù)據(jù)
scanf("%i", &num);
if(num < 0 ){
// 用戶輸入不合法
return test();
}
return num;
}
-
循環(huán)和遞歸的區(qū)別
- 能用循環(huán)實現(xiàn)的功能,用遞歸都可以實現(xiàn);
- 遞歸常用于"回溯", "樹的遍歷","圖的搜索"等問題
- 但代碼理解難度大,內(nèi)存消耗大(易導致棧溢出), 所以考慮到代碼理解難度和內(nèi)存消耗問題, 在企業(yè)開發(fā)中一般能用循環(huán)都不會使用遞歸
設計一個函數(shù)用來計算B的n次方
#include <stdio.h>
int pow(int b, int n);
int main()
{
/*
* 編寫一個函數(shù)實現(xiàn) B的n次方;
* B^n
*
* b^0 = 1;
* b^1 = b;
* b^2 = b*b;
* b^3 = b*b*b;
*
* b^0 = 1;
* b^1 = b^0 * b;
* b^2 = b^1 * b;
*
* 規(guī)律: B^n = b^(n-1) * b;
*
*/
// 定義用戶輸入的變量
int B = -1, n = -1;
// 提示用戶輸入
printf("請輸入兩個數(shù),代表B^n;以逗號隔開\n");
// 接收用戶輸入
scanf("%i,%i", &B, &n);
// 接收結(jié)果
int res = pow(B, n);
printf("res = %i\n",res);
return 0;
}
// 傳入格式: B^n
int pow(int b, int n){
if(n == 0){
return 1;
}else{
return pow(b, n-1) * b;
}
}
- 用遞歸法求N的階乘
#include <stdio.h>
int jieCheng(int n);
int main()
{
/*
* 利用遞歸實現(xiàn)階乘
*
* 3! = 3*2*1;
* 2! = 2*1;
* 1! = 1;
*
* 規(guī)律 n = (n-1)! * n;
*
*/
// 定義變量 n;
int n = -1;
// 提示輸入n
printf("請輸入N的階乘:\n");
// 接收用戶輸入的n
scanf("%i", &n);
// 保存結(jié)果
int res = jieCheng(n);
// 輸入結(jié)果
printf("%i! = %i", n, res);
return 0;
}
int jieCheng(int n){
if(n==1){
return 1;
}else {
return jieCheng(n-1) *n;
}
return n;
}
- 有5個人坐在一起,問第5個人多少歲?他說比第4個人大兩歲砖茸。問 第4個人歲數(shù),他說比第3個人大兩歲隘擎。問第3個人,又說比第2個 人大兩歲。問第2個人,說比第1個人大兩歲凉夯。最后問第1個人, 他說是10歲货葬。請問第5個人多大?
#include <stdio.h>
int getAge(int n);
int main()
{
/*
* 有5個人坐在一起,問第5個人多少歲?他說比第4個人大兩歲。
* 問 第4個人歲數(shù),他說比第3個人大兩歲恍涂。
* 問第3個人,又說比第2個 人大兩歲宝惰。
* 問第2個人,說比第1個人大兩歲。
* 最后問第1個人, 他說是10歲再沧。
* 請問第5個人多大?
*
* 第一個人: 10;
* 第二個人: 10 + 2;
* 第三個人: 10 + 2 + 2;
* 第四個人: 10 + 2 + 2 + 2;
* 第五個人: 10 + 2 + 2 + 2 + 2;
*
* 規(guī)律:(n -1) + 2;
* 其中第幾個人是n, 10就是n的默認值;
*
*
*
*/
// 定義第n個用戶輸入的變量
int n = -1;
// 提示用戶輸入
printf("請輸入一個整數(shù):\n");
// 接收輸入
scanf("%i", &n);
// 定義歲數(shù)
int age = getAge(n);
// 輸出
printf("age = %i", age);
return 0;
}
// 遞歸實現(xiàn)年齡
int getAge(int n){
if(n == 1){
return 10;
}else{
return getAge(n-1) + 2;
}
}