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

C語言標準庫字符串函數手把手實現:strcpy/strcat/strcmp/strlen/strstr/memcpy全解析

     編輯:關於C語言

C語言標準庫字符串函數手把手實現:strcpy/strcat/strcmp/strlen/strstr/memcpy全解析

前言

字符串操作是 C 語言程式設計的高頻需求。標準庫 提供的函數雖然好用,但理解其底層實現邏輯對於掌握指標操作、邊界處理、性能優化至關重要。本文從零手寫 7 個核心字符串/內存函數,附帶詳細註解與測試代碼。

一、字符串基礎認知

  • C 風格字符串 = 字符數組 + '\0' 結束標誌
  • 所有標準函數均以 '\0' 判定字符串邊界
  • const 正確性:源字符串應標記 const,目標字符串不可為 const

二、七大函數逐個擊破

1. strcpy:字符串複製


char* my_strcpy(char* dest, const char* src) {
    assert(dest != NULL && src != NULL);
    char* ret = dest;           // 保存返回目標地址
    
    while ((*dest++ = *src++) != '\0')  // 賦值+判斷一體化
        ;                       // 空循環體
    
    return ret;
}

關鍵點:返回原始 dest 支援鏈式調用;賦值表達式值即為賦值結果,利用此特性合併複製與判斷。

2. strncpy:指定長度複製


char* my_strncpy(char* dest, const char* src, size_t n) {
    assert(dest != NULL && src != NULL);
    char* ret = dest;
    size_t i = 0;
    
    // 複製前 n 個字符或遇到 '\0' 為止
    while (i < n && (*dest++ = *src++) != '\0')
        i++;
    
    // 若 src 長度 < n,補齊 '\0'
    while (i < n) {
        *dest++ = '\0';
        i++;
    }
    
    return ret;
}

注意:標準 strncpy 不保證結果以 '\0' 結尾(當 src 長度 ≥ n 時),使用時需手動補終止符。

3. strcmp:字符串比較


int my_strcmp(const char* s1, const char* s2) {
    assert(s1 != NULL && s2 != NULL);
    
    while (*s1 && *s1 == *s2) {  // 相同且未結束則繼續
        s1++;
        s2++;
    }
    
    // 返回差值:>0 表示 s1>s2,=0 相等,<0 表示 s1

細節:轉為 unsigned char 防止有符號字符比較錯誤(如 0xFF > 0x01 在 signed char 中為負)。

4. strlen:字符串長度


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

遞歸版(教學用,生產環境慎用棧溢出風險):


size_t my_strlen_rec(const char* str) {
    return *str ? 1 + my_strlen_rec(str + 1) : 0;
}

5. strstr:子串查找


char* my_strstr(const char* haystack, const char* needle) {
    assert(haystack != NULL && needle != NULL);
    
    if (*needle == '\0') return (char*)haystack;  // 空串匹配開頭
    
    for (; *haystack; haystack++) {
        if (*haystack == *needle) {  // 找到潛在起始位置
            const char* h = haystack;
            const char* n = needle;
            
            while (*h && *n && *h == *n) {  // 逐字符比對
                h++;
                n++;
            }
            
            if (*n == '\0')  // needle 完全匹配
                return (char*)haystack;
        }
    }
    return NULL;  // 未找到
}

6. strcat:字符串拼接


char* my_strcat(char* dest, const char* src) {
    assert(dest != NULL && src != NULL);
    char* ret = dest;
    
    // 1. 定位到 dest 末尾 '\0'
    while (*dest != '\0')
        dest++;
    
    // 2. 從此處複製 src
    while ((*dest++ = *src++) != '\0')
        ;
    
    return ret;
}

前提:dest 必須有足夠空間容納拼接結果,否則緩衝區溢出。

7. memcpy:內存塊複製(非字符串專用)


void* my_memcpy(void* dest, const void* src, size_t n) {
    assert(dest != NULL && src != NULL);
    
    // 按字節複製,支援任意類型數據
    unsigned char* d = (unsigned char*)dest;
    const unsigned char* s = (const unsigned char*)src;
    
    while (n--)
        *d++ = *s++;
    
    return dest;
}

與 strcpy 差異:memcpy 不關心 '\0',按指定字節數複製,適用於結構體、數組等二進制數據。

三、綜合測試代碼


int main() {
    char str1[100], str2[] = "nihao haha";
    char str3[50] = "Thanks ,thank you.";
    char* s = "you are welcome.";
    
    printf("=== strcpy ===\n");
    my_strcpy(str1, str2);
    printf("Result: %s\n", str1);
    
    printf("\n=== strncpy ===\n");
    my_strncpy(str1, str2, 8);
    str1[8] = '\0';  // 手動補終止符
    printf("First 8 chars: %s\n", str1);
    
    printf("\n=== strcmp ===\n");
    printf("'hello' vs 'hello': %d\n", my_strcmp("hello", "hello"));
    printf("'abc' vs 'abd': %d\n", my_strcmp("abc", "abd"));
    
    printf("\n=== strlen ===\n");
    printf("Length of '%s': %zu\n", str2, my_strlen(str2));
    
    printf("\n=== strstr ===\n");
    char* pos = my_strstr("Hello, world!", "world");
    printf("Found at: %s\n", pos ? pos : "NULL");
    
    printf("\n=== strcat ===\n");
    my_strcat(str3, s);
    printf("Concatenated: %s\n", str3);
    
    printf("\n=== memcpy ===\n");
    struct { int a; double b; } src = {42, 3.14}, dst;
    my_memcpy(&dst, &src, sizeof(src));
    printf("Copied struct: a=%d, b=%.2f\n", dst.a, dst.b);
    
    return 0;
}

四、常見陷阱總結

函數高危場景防範措施
strcpy/strcat目標緩衝區過小使用 strncpy/strncat 或 snprintf
strncpy結果無終止符手動設置 dest[n] = '\0'
strcmp有符號字符比較轉 unsigned char 比較
memcpy內存區域重疊重疊場景用 memmove

五、總結

手寫標準庫字符串函數是鍛煉指標操作能力的最佳練習。核心模式皆為:指標遍歷 + 邊界判斷 + 數據移動。實際開發中應優先使用經過嚴格測試的標準庫實現,但在嵌入式無標準庫環境、需要定制行為(如區分大小寫比較)、或分析性能瓶頸時,理解底層實現至關重要。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved