?
有時候,如果程序的功能比較多。規模比較大,把所有的程序代碼都寫在一個主函數中,就會使得主函數太龐雜,所以為了方便閱讀和維護程序,就引進了 組裝程序 的概念,把某些功能都在其他分支完成,然后需要哪個功能的時候就組裝那個分支到主函數,這些分支就叫它函數,組裝就叫調用,這樣就會使主程序簡化了,哪個函數是做什么也都很清楚。哪里用得到這個功能就在哪個調用就可以了!
?
C 語言的函數可以直觀地辨別出面向過程和面向對象的區別,C 語言的函數有一個特點,就是它有固定的格式和固定的模型。對于一個 C 程序而言,它所有的命令都包含在函數內。每個函數都會執行特定的任務。每個函數都只能被定義一次。但一個函數可以根據需要被多次的聲明和調用。
函數的定義
在使用函數之前必須先定義,后使用。
定義函數要包括:
1、指定函數的數據類型,以便后續返回值的調用
2、指定函數的名字,以便后續調用
3、指定函數形參的類型和名字,以便后續傳遞數據,對于無參(void)可不用
?
函數類型+函數名+(參數表){ 函數體 }
?
參數值的()起到了表示函數調用的重要作用即使沒有參數也需要(),如果有參數,則需要給出正確的數量和順序,這些值會按照一定的順序(看編譯環境一般是從右到左)依次來初始化函數中的參數
注意? 如果調用函數時給的值與參數的類型不匹配有些編譯環境可能會幫你類型強制轉換好,但是有可能不是你想要的那樣。所以? 建議傳遞給函數的值要與聲明一致
?
#include//這里聲明的是int的形參 int a(int a);//但是如果換成浮點類型輸出將會不一樣 int main(void){ double t = 23.8; a(t);//這里傳進去的是浮點數(實參) } int a(int a){//這里接收到的是整型的實參 int t = a; //所以即使換成double t 也一樣 printf("%d", t);//輸出23 } //但是更高級的語言會檢查比較嚴格如 java C++
?
函數的傳值
每個函數都有自己的變量空間,參數也位于這個獨立的空間中,和其他函數沒有關系
值的傳遞:傳遞給函數的值可以是表達式的結果(包括):字面量、變量、函數的返回值、計算的結果。但是在調用函數時,永遠只能傳 值 給函數,在傳值的時候實際上只是把實參的值傳遞到形參處,做的只是一個復制的過程(但是指針就不一樣了)
在函數定義的形參中,它們是不占用內存的,在主函數內調用形參的值時才會被臨時分配內存。實參給形參傳遞的是值的傳遞,屬于單向傳遞。把實參的值傳遞給形參,在調用結束后,形參的存儲單元被釋放,而形參值的任何變化都不會影響到實參的值,實參的存儲單元仍保留并維持數值不變。
地址的傳遞:形參為指針變量時函數之間的數據傳遞,如果函數的形參為指針類型時,對應的實參類型必須與形參保持一致
這種方式使用數組名或者指針作為函數參數,傳遞的是該數組的首地址或指針的值,而形參接收到的是地址,即指向實參的存儲單元,形參和實參占用相同的存儲單元,這種傳遞方式稱為“參數的地址傳遞”。
地址傳遞的特點是形參并不會占用存儲空間,數組名或指針就是一組連續空間的首地址。因此在數組名或指針作函數參數時所進行的傳送只是地址傳送,形參在取得該首地址之后,與實參共同存在一個存儲單元,形參的變化也就是實參的變化。
函數的返回值
在定義的時候,如果不需要返回值的時候定義void類型,其他時候定義其他數據類型。但是使用void類型的時候,不能使用帶值的return(可以沒有return),調用的時候不能做返回值的賦值
int 和void的不同:
? ? 前面加有void 的函數,不能返回任何數據,這類函數應該將所有應該實現的功能在本函數內全部實現。但是并不是不能與外部交換數據,仍然可以通過引用型參數傳遞數據,只是調用時不能直接接受返回值,因為就沒有返回值。
? ? 前面有int的函數,返回值是整型數,可能是結果是整數的數,也可能是運行狀態,成功或失敗的標識,函數調用時可以直接利用返回信息,實現一些功能。
main()函數的揭秘?
main()函數原形:int main(int argc,char const*argv [ ] )
里邊的兩個參數,允許從執行環境中傳遞任意的多字節字符串 (argv[0])是命令本身,命令行參數是保存在argv[? ]里的,C/C++語言規定,可執行程序程序本身的文件名和地址? ,其中的一個描述了命令行參數的個數,通常稱為argc;另一個是命令行參數的數組,通常稱為argv。命令行參數都是字符串,所以argv的類型是char *? [argc+1]。該程序的名字也作為argv[0]傳進來,這個參數的表總以0結束,也就是說,argv[argc]==0。(argv數組的最后一個元素存放了一個NULL的指針)
?
#includeint main(int argc, char * argv[]){ //argv[0]== 調用函數時使用的程序名和地址 //argv[1]==參數1 //argv[2]==參數2 //argv[3]==參數3 //依次類推... //argc 就是計算并保存總共有多少個參數的 int i; for (i = 0; i < argc; i++) { printf("%s ", argv[i]); } return 0; }
?
給main函數傳遞的這兩個參數,argc和argv。argc是int類型的,它表示的是命令行參數的個數。不許要用戶傳遞,它會根據用戶從命令行輸入的參數個數,自動確定。argv是char**類型的,它的作用是存儲用戶從命令行(黑窗口輸入)傳遞進來的參數。它的第一個成員是用戶運行的程序名字。
main函數的返回值,用于說明程序的退出狀態。如果返回0,則代表程序正常退出;返回其他數字的含義則由系統決定,通常,返回非零代表程序異常退出。(一般是由return返回)
return 的妙用
(return 是C語言的關鍵字)函數定義為什么樣的返回類型,該函數中return后就應該是相應類型的值。
在函數中,如果碰到return 語句,那么程序就會返回調用該函數的下一條語句執行,也就是說跳出函數的執行,回到原來的地方繼續執行下去。但是如果是在主函數(main)中碰到return語句,那么整個程序就會停止。return表示從被調函數返回到主調函數或其他函數繼續執行,返回時可 附帶一個返回值,返回值可以是一個常量,變量,或是表達式。? 傳指針形式:直接傳給函數的是變量的地址,由于被調函數在參數指針的作用域之內,此時直接改變變量的本體。
返回值: 計算結果表示函數執行的順利與否(-1、0) 返回值可以為各種數據類型,如:int,float,double,char,a[數組],*a(指針),結構或類。寫return是一種清晰的風格,可以防止一些意外的錯誤。有時候也是想中斷函數執行,返回調用函數處。
返回本地變量的地址是危險的,返回全局變量或靜態本地變量的地址是安全的,返回在函數malloc的內存是安全的,但是容易造成問題,最好的辦法是返回傳入的指針。
在函數中調用函數自己? :即自己return自己 = 遞歸
?
#include//輸出直到n項的斐波那契數列 int add(int n);int i; int main(void) { int n; scanf("%d", &n); int c; for (i = 1; i <=n; i++) {//輸出數列 c = add(i);//調用函數 printf("%d ", c); } return 0; }int add(int n) {//遞歸 if (n == 1) {//第一位數是1 return 1; } else if (n == 2) {//第二位數也是1 return 1; } else { return (add(n-1) + add(n - 2));//第三位開始 等于它前兩位相加 1+1=2 所以第三位是2 } }
?
本地變量和全局變量的補充
本地變量的規則:
本地變量是定義在塊內的:它可以是定義在函數的塊內,也可以是定義在語句的塊內,甚至可以隨便拉一對大括號來定義變量。
但是程序進入這個塊之前,在這個塊內定義的變量它是不存在的,離開這個塊,它也隨之消失。
所以,在塊外面定義的變量在變量仍然有效,然而在塊里邊定義的變量出去塊外邊就無效了。
注意 :如果在塊外邊定義和里邊同名的變量,里邊的變量會覆蓋外邊的值(小覆蓋大的)。
不能再同一個塊內定義同名變量,本地變量不會被初始化,參數再進入函數的時候就被初始化了。
本地變量的規則:
沒有做初始化的全局變量會得到零值,指針會得到NULL,只能用編譯時刻已知道的值來初始化全局變量,它們的初始化再main函數之前。
注意:盡量不要使用全局變量來在函數之間傳遞參數和結果。盡量避免使用全局變量(豐田的案子),使用全局變量和靜態的本地變量是線程不安全的。
審核編輯:湯梓紅
評論
查看更多