陣列內部指標機制
PHP 陣列內部維護一個「指標」(pointer),指向目前元素。預設指向第一個元素。以下函數操作此指標,**不複製陣列、不重置索引**,直接在原陣列上操作 (傳址參考)。
六大核心函數
| 函數 | 作用 | 回傳值 | 指標移動 |
current($arr) | 取得目前元素值 | 值 / false (指標越界) | 不移動 |
key($arr) | 取得目前元素鍵名 | 鍵名 (int/string) / null | 不移動 |
next($arr) | 指標前移一格 | 下一個元素值 / false | 移至下一個 |
prev($arr) | 指標後退一格 | 上一個元素值 / false | 移至上一個 |
end($arr) | 指標移至最後 | 最後元素值 / false | 移至最後 |
reset($arr) | 指標重置至第一個 | 第一個元素值 / false | 移至第一個 |
基本操作範例
<?php
$contact = [
'ID' => 1,
'姓名' => '高某',
'公司' => 'A公司',
'地址' => '北京市',
'電話' => '(010)98765432',
'EMAIL' => 'gao@brophp.com',
];
echo "1. 預設指向第一個: " . key($contact) . ' => ' . current($contact) . "\n";
// ID => 1
echo "2. 再次 current 不移動: " . key($contact) . ' => ' . current($contact) . "\n";
// ID => 1
next($contact);
next($contact);
echo "3. next() 兩次 → 第三個: " . key($contact) . ' => ' . current($contact) . "\n";
// 公司 => A公司
end($contact);
echo "4. end() → 最後: " . key($contact) . ' => ' . current($contact) . "\n";
// EMAIL => gao@brophp.com
prev($contact);
echo "5. prev() → 倒數第二: " . key($contact) . ' => ' . current($contact) . "\n";
// 電話 => (010)98765432
reset($contact);
echo "6. reset() → 回到第一: " . key($contact) . ' => ' . current($contact) . "\n";
// ID => 1
?>
進階應用模式
1. 手動迴圈控制 (替代 foreach)
<?php
$arr = ['a' => 1, 'b' => 2, 'c' => 3];
// 從頭遍歷
reset($arr);
while (($val = current($arr)) !== false) {
echo key($arr) . ' => ' . $val . "\n";
next($arr);
}
// 從尾反向遍歷
end($arr);
while (($val = current($arr)) !== false) {
echo key($arr) . ' => ' . $val . "\n";
prev($arr);
}
?>
2. 條件式跳過/提前結束
<?php
$items = [10, 20, 30, 40, 50];
reset($items);
while (($val = current($items)) !== false) {
if ($val > 30) {
echo "找到第一個 > 30: $val\n";
break; // 保留指標位置,後續可繼續
}
next($items);
}
// 指標停在 40,可繼續 next()/prev()
?>
3. 陣列合併時保留指標位置
<?php
$arr1 = ['a' => 1, 'b' => 2];
$arr2 = ['c' => 3, 'd' => 4];
// 錯誤:array_merge 重設數字索引,指標失效
// $merged = array_merge($arr1, $arr2);
// 正確:使用 + 運算子 (鍵名不覆蓋) 或手動合併
$merged = $arr1 + $arr2;
// 或
$merged = $arr1;
foreach ($arr2 as $k => $v) {
if (!isset($merged[$k])) $merged[$k] = $v;
}
?>
關鍵注意事項與陷阱
- foreach 會重置指標:
foreach ($arr as $v) 執行前會隱性呼叫 reset($arr),結束後指標停在最後 (PHP 7+) 或重置 (PHP 5)。不要在 foreach 中混用指標函數。
- 指標越界回傳 false:
current()、next()、prev() 到達陣列邊界外時回傳 false。必須用 === false 判斷,因為元素值可能為 0、''、null。
- 傳址參考:所有指標函數參數皆為參考傳遞 (內部實作),會直接修改原陣列指標。函數簽名雖未標
&,但行為如同傳址。
- 物件陣列:
ArrayObject、Iterator 實作類別有自己的指標機制,不適用 這些函數。
- 效能:指標函數為 C 級實作,極快。但現代 PHP
foreach 編譯優化極好,通常 foreach 更快且可讀性高。
現代替代方案
| 需求 | 推薦做法 |
| 單純遍歷 | foreach ($arr as $k => $v) |
| 需索引/鍵名 | foreach ($arr as $k => $v) |
| 反向遍歷 | foreach (array_reverse($arr, true) as $k => $v) |
| 條件提前跳出 | foreach (...) { if (...) break; } |
| 多次遍歷同陣列 | foreach ($arr as ...) 可重複呼叫,自動重置 |
| 隨機存取 | $arr[$key] 直接索引 |
實用工具函數:安全取得目前鍵值對
<?php
function currentKeyValue(array &$arr): ?array
{
$key = key($arr);
if ($key === null) {
return null;
}
// key() 回傳 null 只有陣列為空時
// current() 回傳 false 可能是值為 false 或指標越界
// 組合判斷:key() !== null 且 current() !== false (或值確實為 false)
$val = current($arr);
return [$key, $val];
}
// 使用
reset($data);
while (($pair = currentKeyValue($data)) !== null) {
[$k, $v] = $pair;
// 處理...
next($data);
}
?>
總結
- 六大指標函數 (
current, key, next, prev, end, reset) 提供底層陣列游標控制。
- 核心特性:**傳址操作原陣列**、**越界回傳 false**、**foreach 會重置/干擾指標**。
- 現代開發 **99% 場景用
foreach 即可**,指標函數適用於:舊代碼維護、特殊游標控制需求、教學理解陣列內部機制。
- 記得用
=== false 嚴格判斷越界,避免值為 0/''/null/false 的元素被誤判。