C 語言指針拼接字符串陷阱:字符串常量不可修改、必須用字符數組作目標緩衝區
經典錯誤:嘗試將字符串拼接到字符串常量指向的內存區,導致未定義行為 (段錯誤/運行時錯誤)。
錯誤代碼分析
char *a = "hello"; // a 指向只讀數據段中的 "hello\0"
char *b = "rabbit"; // b 指向只讀數據段中的 "rabbit\0"
strcat_(a, b); // 嘗試寫入 a 指向的只讀內存 → 崩潰
記憶體佈局解析
| 聲明 | 存儲區 | 可寫性 | 生命週期 |
| `char *p = "lit";` | 只讀數據段 (`.rodata`) | ❌ 只讀 | 程序全周期 |
| `char arr[] = "lit";` | 棧 (局部) / 數據段 (全局) | ✅ 可寫 | 作用域/程序全周期 |
| `char *p = malloc(n);` | 堆 | ✅ 可寫 | 手動 free 釋放 |
正確實現:目標用字符數組
#include <stdio.h>
#include <string.h>
// 安全拼接:目標緩衝區需足夠大
void safe_strcat(char *dest, size_t dest_size, const char *src) {
size_t dest_len = strlen(dest);
if (dest_len + strlen(src) + 1 > dest_size) {
fprintf(stderr, "緩衝區不足\n");
return;
}
strcat(dest, src); // 標準庫函數已處理 \0
}
int main() {
char a[20] = "hello"; // ✅ 棧上字符數組,可寫,大小 20
char *b = "rabbit"; // 源字符串常量無妨
safe_strcat(a, sizeof(a), b);
printf("拼接結果: %s\n", a); // hello rabbit
return 0;
}
手寫 strcat 實現 (教學用)
char* my_strcat(char *dest, const char *src) {
char *d = dest;
while (*d) d++; // 移至 dest 末尾 \0
while ((*d++ = *src++)); // 複製 src 含 \0
return dest;
}
關鍵避坑清單
- 目標緩衝區必須可寫:字符數組 `char buf[N]` 或 `malloc` 分配
- 目標緩衝區必須足夠大:`strlen(dest) + strlen(src) + 1 ≤ size`
- 源字符串可以是常量:`const char *src` 只讀無妨
- 返回值習慣:返回 `dest` 便於鏈式調用
- 生產代碼用標準庫:`strcat`/`strncat`/`strlcat` (BSD) 經過優化測試
編譯器輔助檢測
- GCC/Clang:`-Wwrite-strings` 將字符串常量類型視為 `const char*`,賦值給 `char*` 會警告
- C++ 編譯器:直接報錯 `invalid conversion from 'const char*' to 'char*'`
- 靜態分析工具:Cppcheck、Clang Static Analyzer 可檢測緩衝區溢出風險
- 上一篇文章: C 語言三種循環求 2+4+6+...+100 偶數和:for、while、do-while 完整對比
- 下一篇文章: 嚴禁提供木馬代碼:C 語言惡意代碼分析與防範、合法安全研究路徑
Copyright ©
程式碼之家 All Rights Reserved