指針與數組交互是 C/C++ 初學者最易踩坑的領域。本文通過一個經典錯誤代碼,層層剖析內存洩漏、野指針、釋放錯誤等核心問題,並給出正確寫法與最佳實踐。
int main() {
char a[] = { "I am a bad boy" };
char *pA = new char[sizeof(a)];
pA = a; // 錯誤關鍵點
for (size_t i = 0; i < sizeof(a); ++i) {
std::cout << *pA << std::endl;
++pA; // 修改堆內存指針
}
delete[] pA; // 崩潰點
pA = NULL;
return 0;
}
char *pA = new char[sizeof(a)]; // ① 在堆上分配內存,pA 指向堆起始地址
pA = a; // ② pA 被改指向棧上數組 a,堆內存地址丟失 → 內存洩漏
本意是拷貝字符串內容,實際卻只是改變指針指向。堆內存無法被釋放,造成洩漏。
++pA; // 循環中不斷移動指針
指針最終指向棧數組 a 的結尾(空字符處),而非堆內存起始地址。
delete[] pA; // pA 已非 new 返回的地址 → 堆損壞、程序崩潰
delete[] 必須傳入 new[] 返回的原始地址,否則行為未定義。
int main() {
char a[] = "I am a bad boy";
char *pA = new char[sizeof(a)]; // 分配堆內存
strcpy(pA, a); // 正確拷貝內容(含結束符)
for (size_t i = 0; i < sizeof(a); ++i) {
std::cout << *(pA + i); // 使用指針偏移,不修改 pA 本身
}
delete[] pA; // 釋放原始堆地址
pA = nullptr;
return 0;
}
| 錯誤模式 | 正確做法 | 說明 |
|---|---|---|
pA = a; 指針賦值 |
strcpy(pA, a) 或逐字符拷貝 |
拷貝內容而非地址 |
循環中 ++pA 移動基址指針 |
用 pA[i] 或 *(pA+i) 訪問 |
保持基址指針不變,便於正確釋放 |
new char[strlen(a)] 忽略結束符 |
new char[strlen(a)+1] 或 sizeof(a) |
預留空字符位置,防止字符串函數越界讀取 |
delete pA; 釋放數組 |
delete[] pA; |
數組必須配對使用 new[]/delete[],否則 UB |
std::string 支持賦值?std::string 重載了 operator=,內部實現深拷貝(分配新內存、拷貝內容、更新長度),屬於類封裝行為,原始指針不具備此能力。初學者切勿混淆。
掌握內存所有權轉移與指針算術的本質,是從 C/C++ 初學者進階為可靠系統程序員的關鍵一課。