strcpy、strlen、strcmp 等。char *strcpy(char *dest, const char *src) {
assert(dest && src);
char *ret = dest;
while ((*dest++ = *src++) != '\0');
return ret;
}
關鍵點:返回目的地址鏈式調用;拷貝包含結束符;無長度檢查,調用者保證空間足夠。
size_t strlen(const char *s) {
assert(s);
size_t len = 0;
while (*s++ != '\0') ++len;
return len;
}
關鍵點:不包含結束符;返回 size_t 無符號。
int strcmp(const char *s1, const char *s2) {
assert(s1 && s2);
while (*s1 && *s2 && *s1 == *s1) { ++s1; ++s2; }
return (unsigned char)*s1 - (unsigned char)*s2;
}
關鍵點:轉 unsigned char 避免符號擴展導致錯誤;返回 <0, =0, >0。
char *strcat(char *dest, const char *src) {
assert(dest && src);
char *ret = dest;
while (*dest) ++dest; // 定位結束符
while ((*dest++ = *src++) != '\0');
return ret;
}
char *strchr(const char *s, int c) {
assert(s);
for (; *s != (char)c; ++s)
if (*s == '\0') return NULL;
return (char *)s;
}
char *strrchr(const char *s, int c) {
assert(s);
const char *end = s;
while (*end) ++end; // 先走到結束符
for (--end; end >= s; --end)
if (*end == (char)c) return (char *)end;
return NULL;
}
char *strstr(const char *haystack, const char *needle) {
assert(haystack && needle);
if (!*needle) return (char *)haystack;
for (; *haystack; ++haystack) {
const char *h = haystack, *n = needle;
while (*n && *h == *n) { ++h; ++n; }
if (!*n) return (char *)haystack;
}
return NULL;
}
註:生產級庫常用 KMP/Two-Way/Sunday 算法優化至 O(n+m)。
char *strtok(char *str, const char *delim) {
static char *next = NULL;
if (str) next = str;
if (!next) return NULL;
// 跳過前導分隔符
str = next;
while (*str && strchr(delim, *str)) ++str;
if (!*str) return next = NULL;
// 找到下一分隔符
next = str;
while (*next && !strchr(delim, *next)) ++next;
if (*next) *next++ = '\0';
else next = NULL;
return str;
}
警示:修改原字符串、內部靜態變量、非重入。多線程請用 strtok_r / strsep。
void *memcpy(void *dest, const void *src, size_t n) {
assert(dest && src);
char *d = dest; const char *s = src;
while (n--) *d++ = *s++;
return dest;
}
void *memmove(void *dest, const void *src, size_t n) {
assert(dest && src);
char *d = dest; const char *s = src;
if (d < s) while (n--) *d++ = *s++; // 正向不重疊
else { d += n; s += n; while (n--) *--d = *--s; } // 反向避免覆蓋
return dest;
}
void *memset(void *s, int c, size_t n) {
assert(s);
unsigned char *p = s;
while (n--) *p++ = (unsigned char)c;
return s;
}
memmove 處理重疊是關鍵區別;memset 按字節填充,僅適合 0/-1/字符填充,不可用於整型數組初始化非字節值。
| 陷阱 | 正確認知 |
|---|---|
strcpy 不檢查長度 | 調用者必須保證 dest 空間 ≥ strlen(src)+1 |
strncpy 不自動補 \0 | 若 src 長度 ≥ n,dest 無結束符,需手動 dest[n]='\0' |
strlen 返回 int | 返回 size_t,打印用 %zu |
strcmp 直接減 char | 必須轉 unsigned char 再減,否則負數字符導致錯誤 |
memset(arr, 1, n) 初始化 int 數組 | 每字節置 1,整數變 0x01010101 ≠ 1,嚴禁 |
熟練掌握這些「輪子」的內部原理,不僅應付面試,更能在造新輪子時避開標準庫的歷史包袱與陷阱。建議親手敲一遍,體會指針遞增、空終止判斷、斷言防護的節奏。