到訪次數 | 106888 |
訂閱次數 | 1 |
文章總數 | 353 |
今日文章 | 0 |
回應總數 | 26 |
今日回應 | 0 |
有許多人對於C語言是否有call by address有些爭論, 以為傳送pointer就是call by address, 以下說明之.
原始的C語言發明人K&R 在其原著 "The C programming language" 中即已說明, C語言只有call by value, 這點不須爭論.
在ANSI C的標準文件 (參考: http://eli-project.sourceforge.net/c_html/c.html) 中, 在6.1.2.5小節, 關於Type的敘述中, 有這麼一段話:
A pointer type may be derived from a function type, an object type, or an incomplete type, called the referenced type. A pointer type describes an object whose value provides a reference to an entity of the referenced type. A pointer type derived from the referenced type T is sometimes called ``pointer to T''.
這段話的意思是說, 如果有一個referenced type (例如 int*), 所謂的pointer type的值就是這個entity(例如 int)的reference. 在這裡要強調的, 是這個value的東西.
在 (http://clc-wiki.net/wiki/C_language:Terms:Pass_by_reference) 裡面, 很清楚的說明:
C does not directly support pass by reference because it always uses pass by value, but a programmer can implement pass by reference by passing a pointer to the variable that the programmer wants passed by reference.
換句話說, 利用pointer來傳遞位址, 只是實現call by reference的方法, 是把address當成value傳遞, 骨子裡還是只有call by value的方法而已.
以下用一個程式例來說明: (使用MS visual studio編譯)
// callbyvalue.cpp : 定義主控台應用程式的進入點。
#include "stdafx.h"
void f(int *x)
{
(*x)++; //在這裡用debug看變數x, 可看到x代表的是一個值
printf("x=%x, &x=%x\n",x, &x);
}
void g(int &y) //只有c++才有這種語法, c語言沒有
{
y=3; //在這裡用debug看變數y, 可看到y等於a
printf("&y=%x\n",&y, y);
}
int _tmain(int argc, _TCHAR* argv[])
{
int a=10, *b;
b=&a;
printf("a=%d, b=%x\n",a, b); // 如果 b=0x0051fa44的話
f(b);
printf("a=%d\n",a);
f((int*)0x0051fa44); // 這樣也算是call by reference??
printf("a=%d\n",a);
f(&a);
printf("a=%d\n",a);
g(a); // 還是這樣才算call by reference??
printf("a=%d\n",a);
// g((int&)0x0051fa44); // 這個會產生語法錯誤
getchar();
return 0;
}
以上程式可用單步執行, 到達f((int*)0x0051fa44)時, 將該常數改為實際的b值, 再繼續單步執行
主程式中第一次以a的位址呼叫f(b), 印出x(位址)之後, 假設a的位址為0x0051fa44,
再次以常數方式代入呼叫f((int*)0x0051fa44), 因為常數被翻譯成組合語言時,
是以立即型運算元形式放入累加暫存器, 因此常數並沒有位址 (在compiler過程中,
並沒有進入變數表內, 所以不會有一個變數名稱佔據某一個位址),
由此可知f()函數接受的參數其實就是一個value,
把位址當成value傳遞並不能被稱為call by reference, 只是長得像call by reference而已.
因為是value, 所以可放入CPU的ALU(累加器)中, 因此才可以被運算 (例如p++),
而真正的address是被放入CPU的address generator register中, 而此register並不提供加減乘除運算,
call by reference會以指定變數的位址, 直接以直接定址或間接定址的機械碼, 將該位址直接搬進
address generator register, 並不會經過CPU的累加器, 所以不可以被運算
註: 使用 (int*)對0x0051fa44做type casting並不是必要的, 因為C語言並不是strong type language,
C語言本身並無規定要使用pointer 的 type casting, 但因在MS visual studio會做此檢查, 所以才加上去
試想, 以下的函數該叫做call by甚麼?
f2(int **x)
{
*(*x+1)=8;
}
g()函數才是所謂的 call by reference, 但這是C++才有的語法, C語言沒有
g()函數中不可出現 &y=3 的敘述, &y不可為lvalue, 常數也不可使用於g()函數呼叫引數
如果f()被稱為call by reference, 那為何C++又要另外定義一個call by reference的呼叫方法?
同時, 也沒有任何一個程式語言, 函數的同一個參數可以同時是call by value及call by reference的,
f()函數既可接受常數參數的呼叫, 就必然是call by value, 而不是call by reference.
在所有支援call by reference的程式語言中, 也沒有哪個語言是可以在函數中對該位址進行加減乘除運算
C語言的函數只有call by value的方法, 但是並不支援複雜結構(例如陣列)的call by value,
如果要使用複雜結構的call by value, 程式員必須自己allocate記憶體並進行複製才可以,
compiler不會自動幫程式員做這件事 (長得像, 但不是本尊)