一篇入門級別的C語言函數,值得一看。

上次寫了 C 語言入門級別的指針,這篇寫函數,指針和函數在一起使用,更有意思,這也才能最大程度的發(fā)揮指針的作用。
基本概念
C 語言是一個函數語言,函數相當于”工廠“,具有”來料加工“的能力。
1sqrt(2.0);
2//sqrt()函數,接收 2.0 這個來料,并把它加工成 1.414...這樣的結果。
1、
函數的分類
A、按 main() 和其它函數:主函數、其它函數
B、按作者分:系統(tǒng)函數(庫函數)、用戶自定義函數(我們自己寫的);無論是庫函數,還是用戶自定義函數,其產生過程和調用,都應遵循相同的編程原則;
C、按函數調用關系分:主調函數、被調函數;
D、函數按參數個數劃分:定參函數、不定參函數。
定參函數,參數個數固定;不定參函數,參數個數不確定;定參函數又分為有參函數和無參函數。
1printf("%d%d%d", a, b, c);? //4 個參數
2printf("Hello\n");? //1 個參數
3//所以 printf() 是一個不定參函數
4//若函數無參,則可以用 void 加以明確聲明
5void main(void){}? //名稱為 main 的函數,無返回值,無參數
2、
函數的產生和使用過程(步驟)
A、函數聲明
B、函數定義
C、函數調用
函數聲明:
(1)、確定的函數名稱,函數名稱是用以區(qū)分其它函數的標志,是這個函數的”入口“地址常量。
(2)、說明函數的返回值類型,sqrt(2.0) 的計算結果為 1.414,這個 1.414 就是所謂的返回值;
函數可以沒有返回值,此時該函數返回值類型必須明確的寫成:void;
返回值是由被調函數返回給主調函數的數值;函數若有返回值,最多只能有一個返回值;
(3)、說明函數的參數,參數個數以及每個參數的數據類型;函數在聲明時,可以只說明參數類型,而缺省相關變量。
1int fun(int n);
2int fun(int);
//以上函數聲明都是正確的
函數定義:
就是函數的編程,實現函數的功能,主要包含函數首部和函數體。
函數首部包括:函數返回值類型、函數名稱、以及形參;
函數體是由 {} 括起來的語句組成。
1int fun(int m){}
注意:
(1)、若函數明確聲明不是 void,則該函數必須以 {return 表達式;} 語句結束,并且,表達式類型必須與函數的返回值類型完全一致!
若函數返回值為 void,則可以寫 return; 語句結束,也可以缺省這條語句。
特別強調:return (表達式); 這樣的寫法,在概念上是一種壞習慣,在 C 語言中,xxx() 出現,代表的是對 xxx() 函數的調用,上面的寫法好像就是在說 return 是函數名稱,而實際上,return 是關鍵字!
(2)、關于 return
A、它是函數運行結束的語句,在這條語句后出現的任何其它語句,是絕對不會運行到的。
1if (條件) {
2? ? return ;
3}
4語句1;
5//明顯,只有條件不成立,才能執(zhí)行語句1
6}
B、所謂的函數運行結束的 return(返回),是“返回”到調用它的函數的下一條語句繼續(xù)執(zhí)行。
C、所謂的 return 表達式; 意思是,返回到主調函數,并用“表達式”的值,替代主調函數中,關于這個函數的調用部分內容。
函數調用:
1#include <stdio.h>
2
3int add (int a, int b);? //函數聲明
4int add (int a, int b) {? //函數定義
5? ? return a + b;
6}
7int main (void) {
8? ? int a;
9? ? int b;
10? ? int sum;
11
12? ? scanf("%d%d", &a, &b);
13? ? sum = add(a, b);? //函數調用
14
15? ? return 0;
16}
形參與實參
1、
形參與實參的定義
A、形參出現在函數定義的“函數首部”的括號中;
B、實參出現在函數調用的括號中;
C、實參有三種形式:常量、變量、表達式;
D、形參只有一種形式:變量。
1int fun (int num);
2int fun (int num) {? //這個 num 就是形參? 這塊是函數定義
3? ...
4}
5
6int main (void) {
7? ? int a;
8
9? ? a = fun(5);? //這個 5 就是實參? 這塊是函數調用
10}
形參只有變量這一種形式,不可能出現常量,甚至不可能出現數組!
2、
(1)、形參與實參之間的關系
A、個數一致;
B、按位置關系必須對應;
C、數據類型必須一致;
D、值傳遞
將實參表達式的值,復制一份,賦值給形參變量!
(2)、私有變量
在一個函數的函數體中定義的變量,以及該函數的所有形參變量,都是這個函數的私有變量。
一個函數只能使用、調用這個函數的私有變量和“公有變量”,絕對不能直接使用其它函數的私有變量(包括數組)。
私有變量是在定義這個變量的函數被調用時,才申請存儲空間;私有變量(尤其是形參變量)的空間,與其它函數的私有變量的空間,是沒有任何關系的,即,其空間是獨立申請的。
私有變量將隨著定義它的函數的運行結束,而釋放其空間。
(3)、形參不能修改實參的值
被調函數對形參變量所進行的任何修改,絕對不會更改與其對應的實參的值,這就是“值傳遞”的必然結果?。?!
對形參變量所進行的任何修改,絕對不會更改實參表達式的值 !
C 語言中,“指針”存在的理由:
最核心的原因:C 語言是一個函數語言,沒有過程這個概念,就需要指針傳變量的地址,才能對變量進行修改!
函數特點
1、
C 語言函數的重要特點
A、高度的獨立性:任何函數只能引用屬于自己的私有變量和全局變量,絕對不能直接引用其它函數的私有變量;這意味著,不能在被調函數中,直接修改主調函數的私有變量的值;
B、函數返回值最多只有一個,若需要通過被調函數同時得到多個結果,那是行不通的;
C、函數的參數(實參與形參)的關系,在 C 語言函數中,只有一種:值傳遞(傳遞的是值,只不過這個值可能是普通的值,也可能是地址值),且這種傳遞方式所產生的必然結果是:對被調函數的形參變量所進行的任何更改,絕對不會影響主調函數的實參的值!
上述三個特點,將對 C 語言函數的功能產生巨大的束縛:在上述情況存在的前提下,C 函數是無法實現:“同時更改主調函數中多個私有變量的值”的功能!
2、
指針的概念,就是為了解決上述 C 語言“致命”問題而提出來的!
指針解決上述問題的具體方法:
A、將需要更改的變量(空間)的首地址作為實參;
B、被調函數對應的形參變量必須是上述變量的指針類型進行接收,這將使被調函數的形參變量“指向”主調函數中,需要更改的那個變量;
C、在被調函數中,通過 * (指向)運算,間接地修改主調函數相關變量的值。
1void fun (int *);
2void fun (int *num) {
3? ? *num = 5;? //通過指針修改 a 空間的值,進行了指向運算
4}
5
6int main (void) {
7? ? int a = 6;
8
9? ? fun(&a);? //傳的是 a 空間的地址
10? ? printf("%d\n", a)? //輸出的結果是 5
11}
變量生命域與作用域
1、
全局變量與局部變量
(1)、全局變量的定義是在 {} 之外進行的;在 {} 之內定義的變量和所有的形參變量都是局部變量;
(2)、全局變量能被那些在書寫順序上,其后定義的函數所引用;局部變量只能被定義它的函數所引用;
(3)、全局變量的空間是在主函數運行之前申請的,且在主函數運行結束后(即,整個程序運行完畢后)才釋放空間的;局部變量是在定義它的函數被調用時才申請空間,且隨著函數運行結束而釋放空間;
(4)、全局變量在定義后賦值前,其值由 C 語言強制賦值為 0;局部變量在定義后賦值前,其值為垃圾;
(5)、全局變量的使用,可使程序設計變得方便,但這種方便會帶來函數之間“耦合”程度的增加,不利于大中型軟件開發(fā),不利于團隊開發(fā),也不利于軟件的維護;全局變量的使用必須謹慎,這是《軟件工程》思想所推薦的。
2、
變量存儲類型
自動存儲類:auto (可缺?。﹊nt a; <=> auto signed int
靜態(tài)存儲類:static
寄存器存儲類:register
外部存儲類:extern
(1)、自動存儲類變量和靜態(tài)存儲類變量的定義:
1int a;
2static int b;
(2)、靜態(tài)存儲類變量是在主函數運行前申請空間,且在整個程序運行結束后釋放;自動存儲類變量是在定義它的函數被調用時才申請空間,且在定義它的函數運行結束后,釋放其空間;
(3)、靜態(tài)存儲類變量在定義后未賦值前,其值被 C 語言強行賦值為 0;自動存儲類變量在定義后未賦值前,其值為垃圾值;
(4)、靜態(tài)存儲類變量,如果是局部變量,則只能被定義它的函數所引用;
(5)、若某函數被多次調用,且該函數定義有靜態(tài)存儲類變量;則,在調用該函數結束后,它所定義的自動存儲類變量的空間都被釋放,而靜態(tài)存儲類變量的空間是不釋放的;在下一次該函數被調用時,靜態(tài)存儲類空間的值是在上一次調用結束的基礎上進行再一次引用的,因此,靜態(tài)存儲類變量具有“累加”效應。
函數遺留問題
一維數組名稱作為實參、二維數組名稱作為實參,形參該以何種數據類型接收的呢?又怎么使用呢?
函數的遞歸調用,也就是常見的遞歸算法有哪些條件呢?遞歸又該如何實現?有哪些應該的場景需要遞歸呢?
在操作系統(tǒng)底層源碼中好多寫法就是函數指針,指向函數的指針,那么指針函數和函數指針又該怎么理解呢?怎么使用呢?
針對這些遺留問題,有興趣的可以自己先去思考、去學習,去研究,我在寫 C 進階之旅的時候,都會寫到。
本篇文章僅僅是把 C 語言的函數帶入門,以及說明了為什么要有指針,指針的出現就是為了函數中的傳參,學習 C 語言之旅,路還很長,琢磨透徹,在敲代碼實現,總會有進步的!