內存操作函數(不關心字符串結構)
| 函數 | 原型 | 語義 | 關鍵區別 |
memcpy | void *memcpy(void *dest, const void *src, size_t n) | 拷貝n字節,src與dest不得重疊 | 最快,無重疊檢查 |
memmove | void *memmove(void *dest, const void *src, size_t n) | 拷貝n字節,正確處理重疊 | 內部自動判斷方向拷貝 |
memset | void *memset(void *s, int c, size_t n) | 將s前n字節置為c(轉unsigned char) | 清零結構體/數組首選 |
memcmp | int memcmp(const void *s1, const void *s2, size_t n) | 比較前n字節,返回<0/=0/>0 | 按無符號字節比較 |
字符串操作函數(以\0結束)
| 函數 | 原型 | 語義 | 返回值 |
strcpy | char *strcpy(char *dest, const char *src) | 拷貝含\0的字符串,dest不可重疊 | dest |
strncpy | char *strncpy(char *dest, const char *src, size_t n) | 拷貝最多n字符,不足補\0,可能無\0結尾 | dest |
strcat | char *strcat(char *dest, const char *src) | 追加src到dest末尾,dest空間需足夠 | dest |
strncat | char *strncat(char *dest, const char *src, size_t n) | 追加src前n字符,保證\0結尾 | dest |
strcmp | int strcmp(const char *s1, const char *s2) | 字典序比較(ASCII值),至\0或差異處 | <0/=0/>0 |
strncmp | int strncmp(const char *s1, const char *s2, size_t n) | 比較前n字符 | <0/=0/>0 |
strchr | char *strchr(const char *s, int c) | 查找字符c首次出現位置 | 指針/NULL |
strrchr | char *strrchr(const char *s, int c) | 查找字符c最後一次出現位置 | 指針/NULL |
strstr | char *strstr(const char *haystack, const char *needle) | 查找子串首次出現 | 指針/NULL |
strlen | size_t strlen(const char *s) | 計算字符串長度(不含\0) | 長度 |
大小寫不敏感比較(POSIX擴展)
int strcasecmp(const char *s1, const char *s2); // 全長比較
int strncasecmp(const char *s1, const char *s2, size_t n); // 前n字符
安全編程紅黑榜
✅ 推薦使用
snprintf(dest, size, fmt, ...) —— 有界格式化輸出
strncpy_s / strcat_s / sprintf_s (C11 Annex K / MSVC) —— 邊界檢查版
memmove 替代 memcpy 當重疊可能性>0時
- 自定義
strlcpy/strlcat (BSD風格) —— 保證\0結尾,返回源長度
❌ 禁用/慎用
gets —— 無邊界檢查,C11已移除
strcpy/strcat/sprintf —— 緩衝區溢出溫床
strncpy —— 易導致無\0結尾字符串,後續操作崩潰
strtok —— 非線程安全,修改源字符串,改用strtok_r
常見陷阱實例
// 陷阱1:strncpy不保證\0結尾
char buf[8];
strncpy(buf, "12345678", 8); // buf = {'1','2','3','4','5','6','7','8'} 無\0!
printf("%s", buf); // UB:讀越界直到隨機遇到\0
// 修正:
buf[sizeof(buf)-1] = '\0'; // 強制終止
// 或用 snprintf(buf, sizeof(buf), "%s", src);
// 陷阱2:strcmp返回值誤判
if (strcmp(a, b)) // ❌ 相等返回0(假),不等返回非0(真) —— 邏輯反了
if (strcmp(a, b) == 0) // ✅ 相等
if (strcmp(a, b) != 0) // ✅ 不等
// 陷阱3:memset非字節填充
int arr[10];
memset(arr, 1, sizeof(arr)); // ❌ 每字節置1 → 0x01010101 = 16843009
// 正確:循環賦值或用填充函數
性能提示
memcpy/memset通常優化為SIMD指令(SSE/AVX/NEON),對齊內存時極快
- 小塊拷貝(<64B)編譯器常內聯為指令序列,無函數調用開銷
- 字符串函數難以向量化(\0終止不定長),長字符串考慮手寫SIMD或
strlen先算長再memcpy