當前位置:才華齋>計算機>C語言>

C語言函式的基本學習教程

C語言 閱讀(1.61W)

C 語言中的函式等價於 Fortran 語言中的子程式或函式,也等價於 Pascal 語言中的過程或函式。函式為計算的封裝提供了一種簡便的方法,此後使用函式時不需要考慮它是如何實現的。使用設計正確的函式,程式設計師無需考慮功能是如何實現的,而只需知道它具有哪些功能就夠了。在 C 語言中可以簡單、方便、高效地使用函式。我們經常會看到在定義後僅呼叫了一次的短函式,這樣做可以使程式碼段更清晰易讀。

C語言函式的基本學習教程

到目前為止,我們所使用的函式(如 printf、getchar 和 putchar 等)都是函式庫中提供的函式。現在,讓我們自己動手來編寫一些函式。C 語言沒有像 Fortran 語言一樣提供類似於**的求冪運算子,我們現在通過編寫一個求冪的函式 power(m, n)來說明函式定義的方法。power(m, n)函式用於計算整數 m 的 n 次冪,其中 n 是正整數。對函式呼叫 power(2,5)來說,其結果值為 32。該函式並非一個實用的求冪函式,它只能處理較小的整數的正整數次冪,但這對於說明問題已足夠了。(標準庫中提供了一個計算 xy 的函式 pow(x, y)。)

下面是函式 power(m, n)的定義及呼叫它的主程式,這樣我們可以看到一個完整的程式結構。

#includeint power(int m, int n);/* test power function */main(){ int i; for (i = 0; i < 10; ++i) printf("%d %d %dn", i, power(2,i), power(-3,i)); return 0;}/* power: raise base to n-th power; n >= 0 */int power(int base, int n){ int i, p; p = 1; for (i = 1; i <= n; ++i) p = p * base; return p;}

函式定義的一般形式為:

返回值型別 函式名(0 個或多個引數宣告){ 宣告部分 語句序列}

函式定義可以以任意次序出現在一個原始檔或多個原始檔中,但同一函式不能分割存放在多個檔案中。如果源程式分散在多個檔案中,那麼,在編譯和載入時,就需要做更多的工作,但這是作業系統的`原因,並不是語言的屬性決定的。我們暫且假定將 main 和 power 這兩個函式放在同一檔案中,這樣前面所學的有關執行 C 語言程式的知識仍然有效。

main 函式在下列語句中呼叫了兩次 power 函式:printf("%d %d %dn", i, power(2, i), power(-i, 3)); 每次呼叫時,main 函式向 power 函式傳遞兩個引數;在呼叫執行完成時,power 函式向 main 函式返回一個格式化的整數並列印。在表示式中,power(2, i)同 2 和 i 一樣都是整數

power 函式的第一行語句 int power(int base, int n) 宣告引數的型別、名字以及該函式返回結果的型別。power 函式的引數使用的名字只在 power 函式內部有效,對其它任何函式都是不可見的:其它函式可以使用與之相同的引數名字而不會引起衝突。變數 i 與 p 也是這樣:power 函式中的 i 與 main 函式中的 i 無關。

我們通常把函式定義中圓括號內列表中出現的變數稱為形式引數,而把函式呼叫中與形式引數對應的值稱為實際引數。

power 函式計算所得的結果通過 return 語句返回給 main 函式。關鍵字 return 的後面可以跟任何表示式,形式為: return 表示式;

函式不一定都有返回值。不帶表示式的 return 語句將把控制權返回給呼叫者,但不返回有用的值。這等同於在到達函式的右終結花括號時,函式就“到達了盡頭”。主調函式也可以忽略函式返回的值。

讀者可能已經注意到,main 函式的末尾有一個 return 語句。由於 main 本身也是函式,因此也可以向其呼叫者返回一個值,該呼叫者實際上就是程式的執行環境。一般來說,返回值為 0 表示正常終止,返回值為非 0 表示出現異常情況或出錯結束條件。為簡潔起見,前面的 main 函式都省略了 return 語句,但我們將在以後的 main 函式中包含 return 語句,以提醒大家注意,程式還要向其執行環境返回狀態。

出現在 main 函式之前的宣告語句 int power(int m, int n); 表明 power 函式有兩個 int 型別的引數,並返回一個 int 型別的值。這種宣告稱為函式原型,它必須與 power 函式的定義和用法一致。如果函式的定義、用法與函式原型不一致,將出現錯誤。

函式原型與函式宣告中引數名不要求相同。事實上,函式原型中的引數名是可選的,這樣上面的函式原型也可以寫成以下形式: int power(int, int);

但是,合適的引數名能夠起到很好的說明性作用,因此我們在函式原型中總是指明引數名。

回顧一下,ANSI C 同較早版本 C 語言之間的最大區別在於函式的宣告與定義方式的不同。按照 C 語言的最初定義,power 函式應該寫成下列形式:

/* power: raise base to n-th power; n >= 0 *//* (old-style version) */power(base, n)int base, n;{ int i, p; p = 1; for (i = 1; i <= n; ++i) p = p * base; return p;}

其中,引數名在圓括號內指定,引數型別在左花括號之前宣告。如果沒有宣告某個引數的型別,則預設為 int 型別。函式體與 ANSI C 中形式相同。

在 C 語言的最初定義中,可以在程式的開頭按照下面這種形式宣告 power 函式:int power();

函式宣告中不允許包含引數列表,這樣編譯器就無法在此時檢查 power 函式呼叫的合法性。事實上,power 函式在預設情況下將被假定返回 int 型別的值,因此整個函式的宣告可以全部省略。

在 ANSI C 中定義的函式原型語法中,編譯器可以很容易檢測出函式呼叫中引數數目和型別方面的錯誤。ANSI C 仍然支援舊式的函式宣告與定義,這樣至少可以有一個過渡階段。但我們還是強烈建議讀者:在使用新式的編譯器時,最好使用新式的函式原型宣告方式。

下面給出MFC上的實現:

void CNowaMagic_MFCDlg::OnBnClickedOk(){ // TODO: 在此新增控制元件通知處理程式程式碼 //CDialogEx::OnOK(); //獲得EDIT CEdit* base; CEdit* n; base = (CEdit*) GetDlgItem(IDC_EDIT1); n = (CEdit*) GetDlgItem(IDC_EDIT2); CString str1; CString str2; CString showStr; char tmp[10] = ""; base -> GetWindowText(str1); n -> GetWindowText(str2); //char* pstr = (LPTSTR)LPCTSTR(str1); int my_base = _ttoi(str1); int my_n = _ttoi(str2); int result = power(my_base, my_n); showStr = itoa(result,tmp,10); CString str = _T("乘方運算結果為:"); MessageBox(str + showStr,_T("程式執行結果"),MB_OK); aseBuffer();}int power(int base, int n){ int i, p; p = 1; for (i = 1; i <= n; ++i) p = p * base; return p;}

程式執行結果:

CString轉int可以使用

int my_base = _ttoi(str1);

函式宣告注意要寫到頭函式中。

傳值呼叫與引數

習慣其它語言(特別是 Fortran 語言)的程式設計師可能會對 C 語言的函式引數傳遞方式感到陌生。在 C 語言中,所有函式引數都是“通過值”傳遞的。也就是說,傳遞給被呼叫函式的引數值存放在臨時變數中,而不是存放在原來的變數中。這與其它某些語言是不同的,比如,Fortran 等語言是“通過引用呼叫”,Pascal 則採用 var 引數的方式,在這些語言中,被呼叫的函式必須訪問原始引數,而不是訪問引數的本地副本。

最主要的區別在於,在 C 語言中,被呼叫函式不能直接修改主調函式中變數的值,而只能修改其私有的臨時副本的值。

傳值呼叫的利大於弊。在被呼叫函式中,引數可以看作是便於初始化的區域性變數,因此額外使用的變數更少。這樣程式可以更緊湊簡潔。側如,下面的這個 power 函式利用了這一性質:

/* power: raise base to n-th power; n >= 0; version 2 */int power(int base, int n){ int p; for (p = 1; n > 0; --n) p = p * base; return p;}

其中,引數 n 用作臨時變數,並通過隨後執行的 for 迴圈語句遞減,直到其值為 0,這樣就不需要額外引入變數 i;power 函式內部對 n 的任何操作不會影響到呼叫函式中 n 的原始引數值。

必要時,也可以讓函式能夠修改主調函式中的變數。這種情況下,呼叫者需要向被呼叫函式提供待設定值的變數的地址(從技術角度看,地址就是指向變數的指標),而被呼叫函式則需要將對應的引數宣告為指標型別,並通過它間接訪問變數。

如果是陣列引數,情況就有所不同了。當把陣列名用作引數時,傳遞給函式的值是陣列起始元素的位置或地址——它並不複製陣列元素本身。在被呼叫函式中,可以通過陣列下標訪問或修改陣列元索的值。