PHP 傳值與傳址

物件傳值與傳址 by Value & by Reference

本章節將會學習傳值與傳址的概念(Call by value & Call by reference),以及透過實地演練來觀察兩者之間的差異。

傳値 Call by value

當我們在建立原始變數時(數字、字串、布林),如果建立一個 a 變數,記憶體會提供一個位址存放 a 變數的資訊,假設這個記憶體位址是 0x100。如果這時候建立一個變數 b 並且讓變數 b = a 時,記憶體會另外建立一個獨立的位址(假設是 0x200)存放變數 b 的資訊。也就是說,雖然 a 和 b 的值相同,但其實是存在於兩個不同的記憶體。
如果是使用 Call by value 的方法去操作變數,因為 a 與 b 的記憶體位址是分開的,所以不會互相干擾,這種情況,我們就稱為 By Value。
記下來:
  1. 在 PHP 中,變數的操作預設是傳値(Call by value)
  2. 在 PHP 中,傳遞變數至方法時(Function),預設也是傳值(Call by value)
$a = 'hello';
$b = '';

function testCallByValue($a)
{
    $a = 'world';   // 嘗試替換 $a 的值
}

testCallByValue($a);

echo $a
輸出結果:
hello

傳址 Call by reference

接下來看一下傳址的部分:
假設我們建立一個變數 a 為一個物件 Object 或 Function 時,記憶體會分配一個位址給變數 a(假設記憶體位址為 0x101)。這時候如果建立一個變數 b ,並且把變數 b = a 時,這時候記憶體不會再給變數 b 一個新的位址,而是將變數 b 指向變數 a 的記憶體位址 0x101。由於 a 和 b 實際上是指著相同的記憶體位址,因此 a 的值改變時, b 的值也會改變,這種情況我們就稱為 By Reference。
記下來:
在 PHP 中,Object 和 Function 變數的操作都是使用傳址 call by reference。
class Car
{
    public $name = "";

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        $this->name = $name;
    }
}

$a = new Car ();
$a->setName("toyota");

$b = $a;  // 將變數 b 指向變數 a 的記憶體位址
$b->setName('honda'); // 由於 a 和 b 都在操作相同的記憶體位置,因此變數 b 的更動會影響到變數 a。
echo $a->getName();  // 輸出結果: honda

強制傳址

由上面傳值與傳址的範例中,可以得知
PHP 預設:
變數的操作預設為傳值 Call by value
物件與方法的操作預設為傳址 Call by reference
但是你也可以使用 & 符號來強制讓變數傳址:
範例1: 宣告變數與賦值時使用 & 符號,強制讓變數傳址:
$A = "test";

$BB = $A;
$BB = "new test";

$CCC = &$BB;    // 將變數 CCC 指向變數 BB 的記憶體位置
$CCC = "final test";  

echo "變數A: $A, 變數BB: $BB, 變數CCC: $CCC";
輸出結果為:
變數A: test, 變數BB: final test, 變數CCC: final test=
範例2: 傳遞變數時使用 & 符號,強制讓變數傳址:
$var1 = "foo";
$var2 = "bar";

function changeThem($pama1, &$pama2){ // 將 var2 的記憶體位置傳入方法內
    $pama1 = "FOO";
    $pama2 = "BAR";
}

changeThem($var1, $var2);
print "var1: $var1, var2: $var2";
輸出結果為:
var1: foo, var2: BAR
範例3:傳遞物件至方法(function)
class Foo{

    public $var1;

    function __construct(){
        $this->var1 = "foo";
    }

    public function printFoo(){
        print $this->var1;
    }
}

$foo = new Foo();

changeFoo($foo);
$foo->printFoo();

function changeFoo($foo){
    $foo->var1 = "FOO";
}
輸出結果:
FOO

結論:

在PHP中:
變數預設為傳值 (Call by value)。
物件變數,為傳址 (Call by Reference)。
變數可以使用 & 符號強制傳址。
Variables containing primitive types are passed by value in PHP5.
Variables containing objects are passed by reference.
參考資料:

留言

  1. 請問,第一個範例:
    callByValue();
    echo $b;
    是否應為空字串而非hello?

    回覆刪除
    回覆
    1. 你說得對!
      當年留下不好的範例真慚愧..

      謝謝你的留言,現在已經換上更容易懂的範例了

      刪除

張貼留言

這個網誌中的熱門文章

Git Commit Message 這樣寫會更好,替專案引入規範與範例

Gitlab 合併請求 Merge Request 是什麼?

PHP OO 物件導向基礎教學