標準庫 <string.h> 提供了豐富的字符串操作函數,但在嵌入式開發、面試或教學中,手寫實現這些函數是檢驗指針掌握程度的經典題目。本文將逐步拆解 strcpy(複製)、strcat(拼接)、strcmp(比較)的核心邏輯,並給出健壯的代碼實現。
功能:將源字符串 src 複製到目標字符串 dest,包含結束符 '\0'。返回目標字符串起始地址以支持鏈式調用。
關鍵點:目標空間必須足夠大;使用 assert 校驗指針有效性;利用賦值表達式的返回值特性合併複製與判斷。
#include <stdio.h>
#include <assert.h>
char *my_strcpy(char *dest, const char *src) {
assert(dest != NULL && src != NULL);
char *ret = dest; // 保存起始地址用於返回
while ((*dest++ = *src++) != '\0') {
; // 循環體為空,賦值與判斷在條件中完成
}
return ret;
}
int main(void) {
char arr1[20] = {0};
char *arr2 = "Hello World";
printf("%s\n", my_strcpy(arr1, arr2)); // 輸出 Hello World
return 0;
}
易錯點:若 dest 空間不足會導致緩衝區溢出,實際項目中常用 strncpy 限制長度。
功能:將 src 追加到 dest 末尾,覆蓋 dest 原有的 '\0',最後添加新的 '\0'。
步驟:1. 找到 dest 末尾的 '\0';2. 從該位置開始複製 src。
#include <stdio.h>
#include <assert.h>
char *my_strcat(char *dest, const char *src) {
assert(dest != NULL && src != NULL);
char *ret = dest;
// 1. 移動到 dest 末尾
while (*dest != '\0') {
dest++;
}
// 2. 複製 src
while ((*dest++ = *src++) != '\0') {
;
}
return ret;
}
int main(void) {
char arr1[30] = "Hello ";
char *arr2 = "World";
printf("%s\n", my_strcat(arr1, arr2)); // 輸出 Hello World
return 0;
}
注意:同樣要求 dest 有足夠空間容納拼接結果。
功能:按字典序比較兩字符串。相等返回 0;s1 > s2 返回正數;s1 < s2 返回負數(標準規定為正/負即可,不必強制 1/-1)。
邏輯:逐字符比較,直到出現差異或遇到 '\0'。
#include <stdio.h>
#include <assert.h>
int my_strcmp(const char *s1, const char *s2) {
assert(s1 != NULL && s2 != NULL);
while (*s1 == *s2) {
if (*s1 == '\0') { // 同時到達結尾,完全相等
return 0;
}
s1++;
s2++;
}
// 返回首個不同字符的 ASCII 碼差值
return (unsigned char)*s1 - (unsigned char)*s2;
}
int main(void) {
char *a = "abcd";
char *b = "abc";
printf("%d\n", my_strcmp(a, b)); // 輸出 100 ('d' - '\0')
return 0;
}
關鍵細節:轉為 unsigned char 再相減,避免有符號字符溢出導致未定義行為,符合標準庫規範。
| 函數 | 核心邏輯 | 常見陷阱 |
|---|---|---|
| strcpy | 指針遍歷賦值,利用賦值表達式判斷結束 | 目標緩衝區溢出 |
| strcat | 先定位 dest 末尾,再接 strcpy 邏輯 | 空間不足、忘記覆蓋原 '\0' |
| strcmp | 逐字符比較,返回首個差異的 ASCII 碼差 | 字符符號擴展問題,需用 unsigned char |
手寫這些函數不僅是面試必備,更能深化對 C 語言指針、內存模型、字符編碼的理解。實際工程中請優先使用經過優化的標準庫版本。