【C語言】第三章-函式-2
第2節
函式呼叫
函式呼叫一般有兩種方式,一種是形參不會影響實參的傳值呼叫,另一種是形參會影響實參的傳址呼叫。
傳值呼叫
傳值呼叫是將實參的值傳入函式體中,傳入的不過是實參的副本,不會改變實參。這個在上一節已經講過其中的原因正式因為C語言副本傳參的這個特性,這也為我們帶來了很多麻煩。
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h>//新增標頭檔案 #include <stdlib.h> void Swap(int x, int y)//交換x, y的值 { int tmp = x; x = y; y = tmp; } int main()//主函式,函式入口 { int a = 4; int b = 5; Swap(a, b); printf("a = %d, b = %d", a, b); system("pause"); }
執行後大家會發現,a, b的值並沒有交換,這正是因為我們在這裡只用了傳值呼叫,傳入的副本不會使實參改變。
傳址呼叫
傳址呼叫是將引數的地址進行傳入,其實就是把指標作為引數,之前我們說過地址就像是變數的門牌號,所以當我們將變數的地址傳入的時候,實參就被鎖定了,形參的改變,也會使實參改變。
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h>//新增標頭檔案 #include <stdlib.h> void Swap(int* x, int* y) { int tmp = *x; *x = *y; *y = tmp; } int main()//主函式,函式入口 { int a = 4; int b = 5; Swap(&a, &b); printf("a = %d, b = %d", a, b); system("pause"); }
上面這行程式碼執行後a, b的值就會被交換,然而我們所做的改變不過是將函式的引數型別改為了指標,因此我們如果想要函式能夠改變實參的值,我們就必須將引數的指標傳入,前提是寫一個引數為指標的函式。在CPP中我們會學到比傳址呼叫更加方便的改變實參的方式。
函式的巢狀呼叫和鏈式訪問
巢狀呼叫
巢狀呼叫是構成C語言最基礎的語法,簡單來說就是允許在函式內呼叫其它函式,比如我們在main
函式中呼叫printf
函式,這種方式相信大家都不陌生了。
鏈式訪問
鏈式訪問是在函式引數裡呼叫函式,這種呼叫方式也很簡單,不過是將一個有返回值的函式在另一個函式的引數列表中進行呼叫,執行時會優先呼叫引數列表中的函式然後根據返回值進行判斷外函式如何執行。這兩種呼叫都十分簡單,今後大家會經常用到,在這裡不做過多贅述。
函式的宣告和定義
函式宣告
因為我們在寫函式的時候必須將函式的定義寫在函式呼叫之前(之前我們都是寫在main
函式前的),因此當我們想要將函式定義寫在呼叫之後以達到美觀的易讀的效果時我們要怎麼做呢?
這就要用到函式的聲明瞭,所謂函式的宣告,不過是在函式的定義寫在函式呼叫之後的情況時為了讓編譯器依舊可以找到函式的定義的位置我們需要在呼叫之前所做的一個宣告罷了。
1、宣告要告訴編譯器又一個函式叫什麼,引數是什麼,返回型別是什麼,但是具體函式存在不存在,無關緊要。
2、函式宣告一般出現在函式呼叫之前,滿足先呼叫後使用。
3、函式的宣告一般是放在標頭檔案中的。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//新增標頭檔案
#include <stdlib.h>
int func(int);//函式的宣告可以省略寫引數名
int main()//主函式,函式入口
{
int num = 0;
func(num);
system("pause");
}
int func(int num)
{
/*
something;
*/
return num;
}
在這個例子中,我們將函式的定義寫在了函式呼叫之後,之所以可以這麼做全部都多虧與我們在函式呼叫之前加上了函式的宣告,函式的宣告不是必須的,但是我們一般將函式的宣告寫在標頭檔案中,這樣會使我們的程式碼更加易讀和管理。
標頭檔案
既然談到了函式的定義我們就不得不提一個和函式定義緊密相連的東西,標頭檔案,上文也說過函式一般是定義在標頭檔案中的。
//標頭檔案
#pragma once
int func(int);//函式的宣告可以省略寫引數名
//另一個.c檔案
int func(int num)
{
/*
something;
*/
return num;
}
//主函式所在的檔案
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//新增標頭檔案
#include <stdlib.h>
#include "test.h"
int main()//主函式,函式入口
{
int num = 0;
func(num);
system("pause");
}
可以發現我將自己定義的函式寫在了另外兩個檔案中,而在主函式所在檔案中引入了函式宣告所在的標頭檔案即可使用函數了。這種書寫函式的方式使得我們的程式碼看起來更加整潔。
值得一提的是,在主函式加入我們自己寫的標頭檔案的時候,最好用""
來包含檔名比較好,這樣可以更方便我們更快的找到函式。在標頭檔案中大家還可以看到#program once
這句話,它的作用是保證我們在多次呼叫同一個我檔案的時候不會造成多次呼叫,發生錯誤,可以說是為了彌補C語言標頭檔案使用的弊端,也是標頭檔案必須的。
可能也會有人在其他教材中發現標頭檔案中還有這麼一種書寫格式。
//標頭檔案
#ifndef TEST
#define TEST
int func(int);//函式的宣告可以省略寫引數名
#endif // !TEST
其實這幾個語句的效果和#program once
效果類似,不過這是以前的寫法並且帶著很多的弊端,所以今後大家寫標頭檔案還是放棄使用這種格式。
函式的遞迴
所謂函式的遞迴簡單來說就是在函式內部呼叫他自身,達到一種迴圈呼叫的效果,是很重要的一種程式設計方法,有時使用遞迴設計程式會給我們帶來很多方便,也會使程式執行更加流暢,不過有時依然是迭代來的更加自然。 遞迴的程式設計是使用一種減而治之的思想,從區域性處理考慮到整體處理。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//新增標頭檔案
#include <stdlib.h>
int factor(int num)//遞迴求階乘
{
if (num == 1)//如果數字等於一則返回它本身
{
return 1;
}
return num * factor(num - 1);//num! = num * (num - 1)!
}
int main()//主函式,函式入口
{
printf("3的階乘是:%d!\n", factor(3));
system("pause");
}
上面這個例子就是一個很好的利用遞迴進行的程式設計,在某些方面遞迴就是可以使你的程式碼更好書寫,但是遞迴也在某些方面有著很麻煩的十分不可理喻的效率,比如在利用遞迴求斐波那契數的時候,就會產生大量的重複運算,大大降低了效率。 遞迴是很重要的程式設計思想,要完全掌握這種設計理念還需要今後多多練習。 歡迎大家來我的部落格閒逛:https://misakifx.github.io/