問題代碼片段
void func() {
int *ps = new int(10);
delete[] ps; // 錯誤:應用 delete,非 delete[]
cout << *ps; // 輸出 10(未定義行為)
}
void func2() {
int *p = new int(20);
cout << &p; // 輸出 p 的地址(棧地址),而非指向的堆地址
}
現象一:delete[] ps 後仍能輸出原值
原因
delete[](或 delete)釋放記憶體回收給堆管理器,但不會清零或擦除記憶體內容。
- 該記憶體區塊在被重新分配或覆寫前,原始位元組仍保留,因此解引用「懸空指標」可能仍讀到舊值。
- 此為未定義行為 (UB),不可依賴。不同編譯器、優化等級、執行環境下結果可能不同(崩潰、輸出亂碼、看似正常)。
正確做法
delete ps; // 單一物件用 delete
ps = nullptr; // 置空,避免誤用懸空指標
現象二:cout << &p 輸出不變的棧地址
原因
p 是函數內的局部指標變數,儲存於棧上。
&p 取得的是指標變數本身的地址(棧地址),而非 p 指向的堆記憶體地址(即 p 的值)。
- 函數每次調用,
p 在棧幀中的相對位置固定,故 &p 輸出相同(或極接近)。
正確觀察指向的堆地址
cout << p; // 輸出指標值,即堆地址
cout << &*p; // 等價於 p,解引用再取址
記憶體模型圖解
棧
+------------------+
| 函數參數/返回位址 |
+------------------+
| 局部變數 p ------|-----> 指向
+------------------+ |
v
堆 +----------+
| int(20) | <-- p 的值 (堆地址)
+----------+
總結
delete 釋放記憶體但不清零,懸空指標解引用屬 UB,務必置 nullptr。
&p 是指標變數本身的棧地址;p 才是指向的堆地址。
- 嚴格區分指標變數地址與指標指向的地址,避免除錯誤判。