首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

C/C++ strcpy 運行崩潰深度解析:字串常量區唯讀屬性與棧上陣列區別

     編輯:程式設計綜合問答

崩潰代碼


char *p[3] = {"dsfs", "jasa", "ussg"}; // p 指向字串常量區 (唯讀)
strcpy(*(p+a1), *(p+a2)); // 嘗試寫入唯讀記憶體 → SIGSEGV / Access Violation

記憶體佈局圖解

代碼區 (Text)          : 機器指令
----------------------------
字串常量區 (Literal Pool) : "dsfs", "jasa", "ussg" (唯讀, 共享)
  0x4000: 'd''s''f''s''\0'  ← p[0] 指向這裡
  0x4010: 'j''a''s''a''\0'  ← p[1] 指向這裡
----------------------------
全域/靜態區 (Data/BSS)  : 全域變數、static 變數
----------------------------
堆區 (Heap)             : malloc/new 分配
----------------------------
棧區 (Stack)            : 局部變數、函數幀
  p2[4]  在這裡 (可讀寫)
  main() 幀

為何 strcpy 崩潰而 strlen 正常?

函數操作對常量區影響
`strcpy(dest, src)`**寫入** `dest` 指向的記憶體若 `dest` 指向常量區 → **崩潰**
`strlen(str)`**唯讀** 遍歷計算長度常量區可讀 → **正常**

正確使用模式


// 1. 目標必須是可寫緩衝區 (棧上陣列或 heap)
char dest[20];           // 棧上,可寫
char *src = "hello";     // 常量區,唯讀
strcpy(dest, src);       // ✓ 正確

// 2. 動態分配目標
char *dest = malloc(strlen(src) + 1);
strcpy(dest, src);
free(dest);

// 3. 需修改字串內容時,勿用指針指向常量
// 錯誤: char *p = "hello"; p[0] = 'H';
// 正確: char p[] = "hello"; p[0] = 'H'; // 陣列在棧,內容可改

VC6 特殊行為 (歷史背景)

  • VC6 不強制執行常量區唯讀 (部分配置下允許寫入),但**屬未定義行為**,換編譯器 (GCC/MSVC 新版) 必崩。
  • 現代編譯器 (GCC/Clang/MSVC) 默認將字串常量放入 `.rodata` 段,頁面屬性 `READ_ONLY`,寫入直接觸發硬體異常。

最佳實踐:const 正確性


// 函數參數加 const,編譯器會攔截嘗試修改的代碼
void safe_copy(char *dest, const char *src) {
    // strcpy(dest, src); // dest 無 const,可寫
}

// 字串指針聲明
const char *msg = "hello"; // 明確承諾不修改內容
// char *msg = "hello";     // C++11 起棄用,C 標準允許但危險

調試技巧

  • 崩潰時查看反組裝/記憶體視圖,確認目標地址屬於哪個段 (`.rodata` vs `.stack`)。
  • 啟用編譯器警告 `-Wwrite-strings` (GCC) 或 `/W4` (MSVC),捕獲 `char*` 指向字串常量。
  • 使用 `valgrind` / AddressSanitizer (`-fsanitize=address`) 自動檢測非法寫入。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved