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

手把手實現字符串核心四大函數:strlen、strcpy、strcat、strcmp 的純 C 實現

     編輯:關於C語言

為什麼要手寫這些函數?

標準庫 <string.h> 提供了高優化的版本,但在以下場景手寫實現極有價值:

  • 教學/面試:考察指針運算、數組遍歷、邊界處理基本功。
  • 嵌入式/裸機環境:無標準庫或需裁剪體積時自行實現。
  • 定制行為:如帶長度限制的安全版本、統計調用次數、特定編碼處理等。

函數簽名設計

為避免與標準庫衝突,命名加前綴 my_,並盡量貼近標準語義:

// 返回字符串長度(不含結束符 '\0')
size_t my_strlen(const char *str);

// 將 src 複製到 dest,返回 dest
char *my_strcpy(char *dest, const char *src);

// 將 src 追加到 dest 末尾,返回 dest
char *my_strcat(char *dest, const char *src);

// 比較兩字符串字典序,返回 <0, 0, >0
int my_strcmp(const char *s1, const char *s2);

逐個擊破:核心實現與關鍵點

1. my_strlen — 計算長度

size_t my_strlen(const char *str) {
    const char *p = str;
    while (*p != '\0') p++;
    return p - str; // 指針相減得元素個數
}

要點:用 const char* 保護輸入;指針遍歷比下標 str[i] 更高效(避免每次加法);返回 size_t 匹配標準庫。

2. my_strcpy — 字符串複製

char *my_strcpy(char *dest, const char *src) {
    char *ret = dest; // 保存返回值
    while ((*dest++ = *src++) != '\0') ; // 賦值+判斷+自增一氣呵成
    return ret;
}

要點:*dest++ = *src++ 先賦值後自增;循環條件直接判斷賦值結果是否為 '\0',極其精簡;必須保證 dest 有足夠空間(標準庫行為,不做邊界檢查)。

3. my_strcat — 字符串追加

char *my_strcat(char *dest, const char *src) {
    char *ret = dest;
    while (*dest != '\0') dest++; // 定位到 dest 末尾的 '\0'
    while ((*dest++ = *src++) != '\0') ; // 接上 src
    return ret;
}

要點:先找到 dest 結尾,再執行類似 strcpy 的複製;同樣不檢查緩衝區溢出。

4. my_strcmp — 字符串比較

int my_strcmp(const char *s1, const char *s2) {
    while (*s1 && (*s1 == *s2)) {
        s1++; s2++;
    }
    return (unsigned char)*s1 - (unsigned char)*s2; // 強轉 unsigned char 避免負值溢出
}

要點:同時遍歷,直到字符不同或任一結束;返回兩字符 ASCII 碼差;轉 unsigned char 確保 '\xFF' > '\x00' 邏輯正確(避免有符號 char 溢出導致邏輯反轉)。

安全增強版:帶長度限制(防緩衝區溢出)

// 安全複製:最多複製 n-1 個字符,保證以 '\0' 結尾
char *my_strncpy(char *dest, const char *src, size_t n) {
    char *ret = dest;
    while (n > 1 && (*dest++ = *src++) != '\0') n--;
    if (n > 0) *dest = '\0'; // src 過短時補 '\0'
    return ret;
}

// 安全追加:最多追加 n 個字符,保證以 '\0' 結尾
char *my_strncat(char *dest, const char *src, size_t n) {
    char *ret = dest;
    while (*dest) dest++;
    while (n-- > 0 && (*dest++ = *src++) != '\0') ;
    *dest = '\0';
    return ret;
}

完整可運行測試程序

#include <stdio.h>
#include <string.h> // 對比標準庫

// 上述四個函數實現放這裡...

int main() {
    char buf1[50] = "Hello";
    char buf2[50] = "World";
    char buf3[50] = "";
    const char *s = "Hello";

    printf("strlen: %zu vs %zu\n", my_strlen(s), strlen(s));
    printf("strcpy: %s\n", my_strcpy(buf3, s));
    printf("strcat: %s\n", my_strcat(buf1, buf2));
    printf("strcmp: %d vs %d\n", my_strcmp("abc", "abd"), strcmp("abc", "abd"));
    printf("strcmp: %d vs %d\n", my_strcmp("abc", "abc"), strcmp("abc", "abc"));
    return 0;
}

總結對比表

函數核心邏輯關鍵細節標準庫對應
strlen指針遍歷至 '\0'const 保護、指針相減、size_t 返回strlen
strcpy逐字符賦值含 '\0'返回 dest、無邊界檢查、指釖自增技巧strcpy
strcat定位 dest 末尾再 strcpy兩階段:找尾 + 接上strcat
strcmp同步遍歷比較差值unsigned char 轉換、字典序語義strcmp

避坑清單

  • ❌ 忘記返回 dest(鏈式調用會斷)。
  • ❌ strcmp 直接用 *s1 - *s2 導致有符號溢出錯誤。
  • ❌ 目標緩衝區太小導致溢出(生產環境必用 strncpy/strncat/snprintf)。
  • ✅ 統一用 const 修飾只讀參數,啟用編譯器優化(-O2 下指針版常比下標版快)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved