Call by Value

C 語言的函式呼叫者都是以 Call by Value 的機制將參數值傳遞給函式。 所謂 Call by Value 就是說當函式開始執行的時候, 另外佔用一段屬於它自己的記憶體,並把參數的值從呼叫者那裡抄過來。 因此,在函式中做的任何數值改變,除了明確 return 回去給呼叫者的數值之外, 在函式與其呼叫者之間,即使有同樣名字的變數,它們的存在也是戶不相干的。 以下我們以兩個例子來說明這個機制。 先看一個簡單的例子。


#include <stdio.h>
  
/* 測試 call by value 的變數傳送機制 (test-callvalue.c) */
void test(int);
  
main() {
    int x=2;  
    printf("x of main() at %u\n", &x);
    printf("x of main() before = %d\n", x);
    test(x);
    printf("x of main() after  = %d\n", x);
}
  
void test(int x) {
    printf("    x of test() at %u\n", &x);
    printf("    x of test() before = %d\n", x);
    x = x*x;
    printf("    x of test() after  = %d\n", x);
}

在以上測試中,我們在 main()test() 裡面各定義了一個名叫 xint 型態變數。 而 main()test() 的呼叫者。 在 main() 裡面我們定義 x=2, 在 test() 裡面我們把它重新定義成 x = x*x。 但是因為這兩個 x 所佔的記憶體地址其實不同, 所以它們互不相干。 在我的機器上執行,獲得的結果是
x of main() at 4026530500
x of main() before = 2
    x of test() at 4026530468
    x of test() before = 2
    x of test() after  = 4
x of main() after  = 2
觀察兩個 x 的地址不同,在 test() 中的計算結果, 只要沒有明確地 return 回來,就不會影響 main() 中的值。

其次,我們詳細地說明第二個例子。 回顧 test-ipow.c, 當呼叫 ipow(2, i) 的時候, 是把 base 的值定義成 2,把 exp 的值定義成 i 的值。 也就是相當於在 ipow() 裡面先執行了

base = 2;
exp = i;
這兩個指令。 在 main() 裡面,i 的值並不受 ipow() 的影響。 而當 ipow() 執行結束時, 它把 ans 的值傳回來 main(), 然後就將所有關於 ipow() 的資料從記憶體中清除了。 就好像將 main() 裡面的數值抄到另一張計算紙上, 在那邊作計算,算好了之後,把答案抄回來,然後立刻就扔掉那張計算紙。

說得更技術一點兒。 當一個程式呼叫 ipow(2, i) 的時候, 作業系統在記憶體中保留了某段位址,記錄

base exp i ans
這些變數。當 ipow() 執行了 return ans; 之後, 這些位址就全部被清除了。 當下次又呼叫 ipow(-3, i) 的時候, 再另起爐灶,全部重來一遍。 所以,在前面的 main() 裡面, 一共令 ipow() 的四個變數
base exp i ans
在記憶體中緣生緣滅 20 個輪迴。

這種將數值 從一個函式複製到另一個函式 去執行的作法, 稱為值呼叫 (call by value)。

C 函式的參數,凡是普通變數,都採用值呼叫。
因為 ipow(2, i)ipow(-3, i) 裡面的 i, 只是將它的值複製一份去 ipow() 裡面而已。 這個值在 ipow() 之內不管做了什麼改變,都不關呼叫者 (caller) 的事。 所以在 ipow() 執行結束之後,i 的值不會改變。

說得再詳細一點,在 main() 裡面有一個以 i 為名的變數, 在 ipow() 裡面有一個以 exp 為名的變數。 這兩個變數在記憶體中佔有不同的地址編號。 只有當 ipow() 一開始的時候, 它們的值一樣 (exp 抄了 i 的值過來)。 此後,iexp 是風馬牛不相及的。

基於這個道理,ipow() 可以改寫如下,而不必擔心會影響到 main() 裡面 i 的值。


/* 整數的非負整數次方函式,第二版  */
int ipow(int base, int exp) {
    int ans=1;

    for (; 0<exp; --exp)
        ans *= base;
    return ans;
}

這個函式直接利用 exp 來控制 base 自乘的次數。 那個 for迴圈,每執行因此 exp 的值就減 1。 一直做到 exp=0 為止。例如若 exp 最初的值是 5, 則這個迴圈就執行 5 次,得到 ans 就是 base 的 5 次方。

習題

  1. 寫一個函式
    double ipower(int base, int exp)
    當它遇到 exp 是負整數的時候也會算對 (只要在 double 容許的精度範圍內)。 寫一個 main() 來測試兼示範你的 ipower()

[ 前一節 ]‧[ 後一節 ]‧[ 回目錄 ]



注意:此處所有文件均為原著,個別的版權宣告日後會一一公布, 整體版面設計亦尚未完成。但仍請勿抄襲文字與圖片,以免觸犯著作權法。

Created: Apr 1, 2000
Last Revised: Feb 25, 2001
© Copyright 2001 Wei-Chang Shann 單維彰

shann@math.ncu.edu.tw