首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言字符串操作大全:從反轉到回文判斷的多種實現

     編輯:關於C語言

前言

字符串處理是 C 語言程序設計的核心技能。標準庫雖提供 <string.h>,但理解其底層實現、掌握多種寫法(迭代、遞歸、位運算等)能顯著提升代碼質量與面試競爭力。本文系統梳理 8 大經典操作,附帶多版本代碼與解析。

1. 字符串反轉 —— 6 種寫法

原地反轉,頭尾雙指針交換。

版本 1:while 循環(經典)

void strRev(char *s)
{
    char temp, *end = s + strlen(s) - 1;
    while (end > s) {
        temp = *s;
        *s   = *end;
        *end = temp;
        --end;
        ++s;
    }
}

版本 2:for 循環(緊湊)

void strRev(char *s)
{
    char temp;
    for (char *end = s + strlen(s) - 1; end > s; --end, ++s) {
        temp = *s;
        *s   = *end;
        *end = temp;
    }
}

版本 3:異或交換(無臨時變量)

void strRev(char *s)
{
    for (char *end = s + strlen(s) - 1; end > s; --end, ++s) {
        *s ^= *end;
        *end ^= *s;
        *s ^= *end;
    }
}

版本 4:異或單行版

void strRev(char *s)
{
    for (char *end = s + strlen(s) - 1; end > s; --end, ++s)
        *s ^= *end ^= *s ^= *end;   // 未定義行為風險,僅供演示
}

版本 5:極簡 for(含移動)

void strRev(char *s)
{
    for (char *end = s + strlen(s) - 1; end > s; *s++ ^= *end ^= *s ^= *end--);
}

版本 6:遞歸(只打印不修改)

void strRev(const char *s)
{
    if (*s == '\0') return;
    strRev(s + 1);
    putchar(*s);
}
> ⚠️ 版本 4/5 使用多次無序列點的賦值,屬於未定義行為,**生產代碼請用版本 1/2**。

2. 實現 strcpy —— 3 種風格

核心:從源拷貝至目標,包含結束符 '\0',返回目標地址以支持鏈式調用。

版本 1:while + 後置遞增

char *strcpy(char *dest, const char *src)
{
    char *p = dest;
    while (*dest++ = *src++);
    return p;
}

版本 2:標準庫風格(帶 __cdecl)

char * __cdecl strcpy(char *dst, const char *src)
{
    char *p = dst;
    while (*p++ = *src++);
    return dst;
}

版本 3:下標法(直觀)

void strcpy(char *dest, const char *src)
{
    int i = 0;
    for (; src[i] != '\0'; ++i)
        dest[i] = src[i];
    dest[i] = '\0';
}

3. 實現 atoi —— 字符串轉整數

跳過空白、檢測符號、逐位累加。這裡展示兩種冪計算方式。

版本 1:遞歸求冪

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;
}

版本 2:庫函數 pow

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,健壯性更強。

4. 實現 strlen —— 4 種寫法

版本 1:while

size_t __cdecl strlen(const char *s)
{
    int i = 0;
    while (*s++) ++i;
    return i;
}

版本 2:for

size_t __cdecl strlen(const char *s)
{
    int i = 0;
    for (; *s; ++s, ++i);
    return i;
}

版本 3:遞歸(無額外變量)

size_t __cdecl strlen(const char *s)
{
    return *s ? strlen(++s) + 1 : 0;
}

版本 4:三元運算符壓縮

size_t __cdecl strlen(const char *s)
{
    return *s ? (strlen(++s) + 1) : 0;
}

5. 實現 strcat —— 字符串拼接

char * __cdecl strcat(char *dst, const char *src)
{
    char *p = dst;
    while (*p) ++p;          // 定位 dst 末尾
    while (*p++ = *src++);   // 拷貝 src
    return dst;
}

6. 實現 strcmp —— 字符串比較

錯誤示範:過早返回

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;   // 正/負/零 即大/小/等
}

7. 統計元音字母個數(遞歸)

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;
}

8. 判斷回文(忽略非字母、大小寫)

#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;
}

總結

掌握字符串操作的多種實現,不僅能應對面試手寫代碼,更能深入理解指針、內存與算法邏輯。建議:

  • 生產環境優先使用標準庫函數(經過優化、測試)。
  • 手寫練習時注重邊界條件(空指針、空串、重疊內存)。
  • 遞歸雖優雅,但長字符串可能棧溢出,生產慎用。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved