#include <stdio.h> int main(void) { int month, day, hour, min; char *name; month = 7; day = 27; hour = 9; min = 45; name = "kaiching"; printf("\n\n今天是 %d 月 %d 日\n", month, day); printf("現在是上午 %d:%d\n", hour, min); printf("哈囉, %s!\n\n", name); return 0; } /* 《程式語言教學誌》的範例程式 http://pydoing.blogspot.com/ 檔名:csimple.c 功能:簡單顯示訊息的例子 作者:張凱慶 時間:西元2010年7月 */
我們把注意力放到函數 (function) 定義區中的呼叫 (call) 函數,下圖中程式碼的第 14 行到第 16 行
函數 printf() 為標準函數庫中 stdio.h 所定義的格式化輸出函數,可將格式化字串傳送到標準輸出裝置上,標準輸出裝置通常是指螢幕。所謂的格式化字串是指含有轉換字符的字串,轉換字符如 printf() 第一個參數,也就是格式化字串中的 %d 及 %s 。
函數 printf() 可用的轉換字符如下列表
%d, %i | 有正負號的十進位整數 | int |
%u | 無正負號的十進位整數 | int |
%o | 無正負號的八進位整數 | int |
%x, %X | 無正負號的十六進位整數 | int |
%c | 字元 | char |
%s | 字串 | char * |
%f | 浮點數 | double |
%p | 記憶體位址的編碼 | void * |
%% | 百分比符號 | % |
所以 printf() 在第一個參數後依轉換字符的數列接上引數,每個引數都用逗號 , 隔開。第 14 行及第 15 行的格式化字串各有兩個 %d ,所以後面接相對應整數型態的變數,第十六行的格式化字串有一個 %s ,其後接一個字串,由於字串是個比較複雜的字元陣列,所以這裡是個指向字串的指標變數。
程式中使用已經寫好的函數,不論是標準函數庫中的函數或是自行定義的函數,使用函數都稱之為呼叫。若所呼叫的函數有定義參數,呼叫時便須提供型態相符的引數,這裡須注意,很多文獻都有用到參數或引數的說法,如英文原文參數為 parameter ,引數為 argument ,然而實際上參數是函數定義所用的詞,引數則是呼叫函數所用的詞,也有人用 formal parameter 及 actual argument 區分兩者。這裡我們提醒引數是指什麼,但我們傾向只使用參數一詞。
函數與函數之間靠參數與回傳值來傳遞訊息,呼叫函數傳遞參數給被呼叫函數,被呼叫函數則是用回傳值給呼叫函數,如下圖
由於 C 語言是採用傳值呼叫 (call by value) ,因此呼叫函數時所用參數是傳遞該參數數值的副本,怎麼說呢?我們需要了解程式中所用的每一個變數都具有記憶體位址,因此每個變數都具有該變數所儲存的值,以及該變數的記憶體位址
雖然資料依型態所佔用的記憶體空間(位址)多寡不同,然而這方面編譯器會利用類似表格的方式,記錄每個變數的起始位址。所謂的副本就是將原變數的值拷貝到另一個記憶體空間
因此被呼叫函數所具有的參數為原呼叫函數拷貝的副本,被呼叫函數並不能夠直接修改原呼叫函數所具有的變數值。
此外須留意 C 語言只有傳值呼叫,坊間有些傳參考呼叫 (call by reference) 或是傳址呼叫 (call by address) 的說法。前者,也就是傳參考呼叫,其為 C 之後演生出的 C++ 與 JAVA 才有的呼叫方式,但很多時候 C 與 C++ 籠統的合稱 C 語言,因此有 C 語言具有傳參考呼叫的說法。至於後者,也就是傳址呼叫的說法,嚴格說這樣的講法並不對。
會有傳址呼叫的說法是因為 C 語言可以用指標指向變數的記憶體位址,然而指標所儲存的只是記憶體位址的編碼值,甚至可以對指標進行某些程度的算術運算,實際上指標所儲存記憶體位址的編碼值仍是值。如果強行用傳址呼叫來理解 C 語言函數間參數的傳遞,使用雙重指標的部份容易兜不攏,便會對指標有很難、不易掌握的印象。
問題與討論
- 替簡單例子多宣告一個有關年份的整數變數 year ,然後將今年的年份指派給 year ,最後把變數 year 列印出來。
- 參數與引數有什麼不同?
- C 語言函數呼叫的方式為何?
- 為什麼說傳址呼叫的講法不對?
沒有留言:
張貼留言