字符串處理是 C 語言程序設計的核心技能。標準庫雖提供 <string.h>,但理解其底層實現、掌握多種寫法(迭代、遞歸、位運算等)能顯著提升代碼質量與面試競爭力。本文系統梳理 8 大經典操作,附帶多版本代碼與解析。
原地反轉,頭尾雙指針交換。
void strRev(char *s)
{
char temp, *end = s + strlen(s) - 1;
while (end > s) {
temp = *s;
*s = *end;
*end = temp;
--end;
++s;
}
}
void strRev(char *s)
{
char temp;
for (char *end = s + strlen(s) - 1; end > s; --end, ++s) {
temp = *s;
*s = *end;
*end = temp;
}
}
void strRev(char *s)
{
for (char *end = s + strlen(s) - 1; end > s; --end, ++s) {
*s ^= *end;
*end ^= *s;
*s ^= *end;
}
}
void strRev(char *s)
{
for (char *end = s + strlen(s) - 1; end > s; --end, ++s)
*s ^= *end ^= *s ^= *end; // 未定義行為風險,僅供演示
}
void strRev(char *s)
{
for (char *end = s + strlen(s) - 1; end > s; *s++ ^= *end ^= *s ^= *end--);
}
void strRev(const char *s)
{
if (*s == '\0') return;
strRev(s + 1);
putchar(*s);
}
> ⚠️ 版本 4/5 使用多次無序列點的賦值,屬於未定義行為,**生產代碼請用版本 1/2**。
核心:從源拷貝至目標,包含結束符 '\0',返回目標地址以支持鏈式調用。
char *strcpy(char *dest, const char *src)
{
char *p = dest;
while (*dest++ = *src++);
return p;
}
__cdecl)char * __cdecl strcpy(char *dst, const char *src)
{
char *p = dst;
while (*p++ = *src++);
return dst;
}
void strcpy(char *dest, const char *src)
{
int i = 0;
for (; src[i] != '\0'; ++i)
dest[i] = src[i];
dest[i] = '\0';
}
跳過空白、檢測符號、逐位累加。這裡展示兩種冪計算方式。
int power(int base, int exp)
{
return (exp == 0) ? 1 : base * power(base, exp - 1);
}
int __cdecl atoi(const char *s)
{
int exp = 0, n = 0;
const char *t = NULL;
// 跳過空白
while (*s == ' ' || *s == '\t' || *s == '\n') ++s;
if (*s < '0' || *s > '9') return 0;
// 定位數字結束處
for (t = s; *t >= '0' && *t <= '9'; ++t);
--t;
// 從低位向高位累加
while (t >= s) {
n += (*t - '0') * power(10, exp);
--t;
++exp;
}
return n;
}
int __cdecl atoi(const char *s)
{
int exp = 0, n = 0;
const char *t = NULL;
while (*s == ' ' || *s == '\t' || *s == '\n') ++s;
if (*s < '0' || *s > '9') return 0;
for (t = s; *t >= '0' && *t <= '9'; ++t);
--t;
while (t >= s) {
n += (*t - '0') * pow(10, exp);
--t;
++exp;
}
return n;
}
> 💡 實際工程請直接用標準庫 strtol,健壯性更強。
size_t __cdecl strlen(const char *s)
{
int i = 0;
while (*s++) ++i;
return i;
}
size_t __cdecl strlen(const char *s)
{
int i = 0;
for (; *s; ++s, ++i);
return i;
}
size_t __cdecl strlen(const char *s)
{
return *s ? strlen(++s) + 1 : 0;
}
size_t __cdecl strlen(const char *s)
{
return *s ? (strlen(++s) + 1) : 0;
}
char * __cdecl strcat(char *dst, const char *src)
{
char *p = dst;
while (*p) ++p; // 定位 dst 末尾
while (*p++ = *src++); // 拷貝 src
return dst;
}
int strcmp(const char *a, const char *b) // ❌ 錯誤
{
for (; *a && *b; ++a, ++b)
if (*a > *b) return 1;
else if (*a == *b) return 0; // 相等時直接返回,導致 "ab" == "abc"
else return -1;
return 0;
}
int __cdecl strcmp(const char *src, const char *dst)
{
int ret = 0;
while (!(ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
++src, ++dst;
if (ret < 0) ret = -1;
else if (ret > 0) ret = 1;
return ret;
}
int strcmp(const char *dest, const char *source)
{
assert(dest && source);
while (*dest && *source && *dest == *source) {
++dest; ++source;
}
return *dest - *source; // 正/負/零 即大/小/等
}
int is_vowel(char c)
{
switch (c) {
case 'a': case 'A':
case 'e': case 'E':
case 'i': case 'I':
case 'o': case 'O':
case 'u': case 'U': return 1;
default: return 0;
}
}
int count_vowel(const char *s)
{
if (*s == '\0') return 0;
return is_vowel(*s) + count_vowel(s + 1);
}
int main(void)
{
const char *s = "AobCd ddudIe";
printf("%d\n", count_vowel(s)); // 輸出 6
return 0;
}
#include <ctype.h>
#include <stdbool.h>
bool is_palindrome(const char *s)
{
const char *end = s;
if (*end == '\0') return true;
while (*end) ++end; // 移至結束符
--end; // 最後一個字符
while (s <= end) {
while (s <= end && (*s == ' ' || !isalpha(*s))) ++s;
while (s <= end && (*end == ' ' || !isalpha(*end))) --end;
if (s > end) break;
if (toupper(*s) != toupper(*end)) return false;
++s; --end;
}
return true;
}
int main(void)
{
const char *s = "Madam I'm Adam";
printf("%s %s\n", s, is_palindrome(s) ? "is a palindrome!" : "is not a palindrome!");
return 0;
}
掌握字符串操作的多種實現,不僅能應對面試手寫代碼,更能深入理解指針、內存與算法邏輯。建議: