C 語言初學教材 - 第一章 呼叫

簡單的例子的程式碼如下

#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 語言函數間參數的傳遞,使用雙重指標的部份容易兜不攏,便會對指標有很難、不易掌握的印象。


問題與討論
  1. 替簡單例子多宣告一個有關年份的整數變數 year ,然後將今年的年份指派給 year ,最後把變數 year 列印出來。
  2. 參數與引數有什麼不同?
  3. C 語言函數呼叫的方式為何?
  4. 為什麼說傳址呼叫的講法不對?




沒有留言: