首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C/C++字符串字面量修改導致崩潰:.rodata段只讀屬性深度解析

     編輯:關於C

現象重現

代碼嘗試修改字符串字面量內容:

char *p = "I love you China, do you love me?";
*p = 'X';  // 運行時崩潰:Access Violation / Segmentation Fault

編譯無錯誤,但運行時觸發內存保護異常。

根本原因:內存佈局差異

定義方式存儲區域可修改性生命周期
char *p = "abc";.rodata (只讀數據段)不可修改程序全周期
char p[] = "abc";棧 (Stack)可修改作用域結束

.rodata 段機制

  • 編譯器將字符串字面量存放在只讀數據段(.rodata / .text 段)
  • 作業系統通過MMU設置頁面屬性為只讀(Read-Only)
  • 任何寫入操作觸發頁面錯誤,OS發送 SIGSEGV / EXCEPTION_ACCESS_VIOLATION

正確做法對比

❌ 錯誤:指針指向字面量

char *str = "hello";
str[0] = 'H';  // Crash!

✅ 正確:數組初始化(拷貝到棧)

char str[] = "hello";  // 相當於 char str[6] = {'h','e','l','l','o','\0'};
str[0] = 'H';          // OK,修改棧上副本

✅ 正確:動態分配堆內存

char *str = malloc(6);
strcpy(str, "hello");
str[0] = 'H';          // OK,堆內存可讀寫
free(str);

✅ 正確:const 修飾指針(編譯期攔截)

const char *str = "hello";
// str[0] = 'H';  // 編譯錯誤:assignment of read-only location

深度解析:為什麼 C 標準允許這種「不安全」寫法?

  1. 歷史包袱:早期 C(K&R C)無 const 關鍵字,字面量類型為 char* 而非 const char*
  2. 兼容性:修改標準會破壞海量存量代碼
  3. 效能考量:字面量共享(String Interning)節省內存,如多處使用 "error" 只存一份

現代編譯器(GCC/Clang/MSVC)均提供編譯警告:-Wwrite-strings 會將字面量類型視為 const char*,賦值給非 const 指針時發出警告。

調試技巧:定位只讀內存寫入

  • GDB:watch *(char*)0xADDRESS 監視內存寫入
  • Valgrind:--track-origins=yes 追蹤非法寫入源頭
  • AddressSanitizer:編譯加 -fsanitize=address 精準報錯

最佳實踐總結

  1. 習慣加 const:const char *msg = "text"; 讓編譯器幫你擋錯
  2. 需修改時用數組:char buf[] = "text"; 自動分配可寫副本
  3. 動態長度用堆:malloc/strdup 並記得 free
  4. 開啟編譯器警告:-Wall -Wextra -Wwrite-strings (GCC/Clang) 或 /W4 (MSVC)

擴展知識:C++ 的特殊規則

C++ 標準規定字符串字面量類型為 const char[N],因此:

char *p = "hello";  // C++11 起廢棄,C++20 移除(編譯錯誤)
const char *p = "hello";  // 標準寫法
auto p = "hello";         // 推導為 const char*

這是 C/C++ 互操作時的常見坑點之一。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved