C 標準庫提供四種字符串/記憶體複製函數:strcpy、strncpy、memcpy、memmove。它們在複製行為、空字符(\0)處理、重疊記憶體安全性上有顯著差異。本文通過實例與記憶體地址分析,深度剖析各函數特性與最佳實踐。
| 函數 | 原型 | 複製單位 | 遇 \0 停止 | 補 \0 | 重疊安全 |
|---|---|---|---|---|---|
| strcpy | char *strcpy(char *s1, const char *s2) | 字符 | 是 | 是(複製含 \0) | 否 |
| strncpy | char *strncpy(char *s1, const char *s2, size_t n) | 字符 | 否(固定 n) | 不足 n 補 \0 | 否(舊標準) |
| memcpy | void *memcpy(void *s1, const void *s2, size_t n) | 位元組 | 否 | 否 | 否(舊標準) |
| memmove | void *memmove(void *s1, const void *s2, size_t n) | 位元組 | 否 | 否 | 是 |
strcpy(s1, s2) 複製含結尾 \0 的整串字符。若 s1 空間不足,會溢出覆蓋後續記憶體(「躺槍」現象)。
實例:s1(8 位元組)複製 s2(11 字符),超出 3 字節污染緊鄰的 s3。
strncpy(s1, s2, n) 複製前 n 字符:
s2 長度 ≥ n:不補 \0,s1 非合法字符串,輸出時會讀到後續記憶體。s2 長度 < n:複製完 s2 後,補 \0 至 n 字節。最佳實踐:n = sizeof(s1) - 1,複製後手動 s1[n] = '\0'。
行為同 strncpy(固定 n 字節,不關心 \0),但適用於任意類型資料(結構體、陣列等),非字符串專用。
當 s1 與 s2 記憶體區域重疊時,strcpy、strncpy、memcpy 行為未定義(舊標準)。memmove 內部處理重疊(如由後向前複製),保證正確。
實例驗證:strcpy(s2, s2+1) 結果異常;memmove(s2, s2+1, n) 正確右移字符串。
注:現代 C 庫(glibc 等)已優化strncpy/memcpy支援重疊,但標準未保證,跨平台仍首選memmove。
strcpy 或 strncpy + 手動加 \0。strncpy(dest, src, sizeof(dest)-1); dest[sizeof(dest)-1] = '\0';memcpy(非重疊)或 memmove(可能重疊)。memmove,性能差異可忽略。標準庫函數的演進體現了「先收錄廣泛使用的舊函數,再提供改進版」的務實哲學。理解其歷史包袱與設計權衡,才能寫出健壯、可移植的 C 代碼。