理解語言的值類型賦值語義,對預測內存行為、避免副作用至關重要。PHP 採用 Copy-on-Write (COW),而 C# 值類型採用 立即深拷貝。本文從底層機制、代碼演示、性能影響三個維度深度對比。
| 特性 | PHP (標量/數組) | C# (結構體/基本類型) |
|---|---|---|
賦值操作 $b = $a | 引用計數 +1,共享內存(指向同一 zval) | 立即在棧上分配新內存,逐位複製值 |
| 修改觸發複製 | 寫入時($a = 5)才分離內存 | 賦值瞬間已完成複製,後續修改互不影響 |
| 內存效率 | 只讀場景極高(零拷貝) | 每次賦值必拷貝,大結構體有開銷 |
| 引用語義 | &$b = $a 強制綁定 | ref/out 關鍵字顯式傳引用 |
<?php
$a = 2;
$b = $a; // 此時 $b 與 $a 指向同一 zval(refcount=2)
echo "修改前: \$a=$a, \$b=$b\n"; // 2, 2
$a = 5; // 寫入觸發分離:為 $a 分配新 zval,值複製為 5;$b 仍指向舊 zval(2)
echo "修改後: \$a=$a, \$b=$b\n"; // 5, 2
// 數組同理
$arr1 = [1,2,3];
$arr2 = $arr1; // 共享底層 Bucket 數組
$arr1[0] = 99; // 寫時複製:分離數組
var_dump($arr1, $arr2); // arr1[0]=99, arr2[0]=1
?>
// C#
int a = 2;
int b = a; // 立即在棧上為 b 分配 4 字節,複製值 2
a = 5; // 僅修改 a 的棧槽,b 仍為 2
// 結構體同理
struct Point { public int X, Y; }
Point p1 = new Point { X = 1, Y = 2 };
Point p2 = p1; // 立即逐字段複製(淺拷貝,字段若為引用類型則共享引用)
p1.X = 99; // p2.X 仍為 1
zval 結構體(含類型、值、refcount、is_ref)refcount++,指針指向同一 zvalrefcount > 1 → 分離:refcount--,分配新 zval,複製值,修改新 zvalHashTable,同樣 COW,修改鍵值觸發分離int、struct、enum)直接存儲在棧上memcpy 級),無引用計數概念ref 傳地址class)引用存棧,對象存堆,賦值複製引用foreach ($arr as &$v) 會導致原數組被修改,需 unset($v) 清理struct 建議 < 16 字節、不可變、邏輯單一,避免頻繁拷貝開銷總結:PHP 的 COW 優化了「讀多寫少」場景,C# 的立即複製保證了值語義的直觀性與線程安全。理解差異,才能寫出符合語言習慣的高性能代碼。