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

C/C++ 標準庫字符串處理函數完全指南:strcpy、strcat、strcmp、strtok、strlen 實戰詳解

     編輯:關於C++

C/C++ 標準庫 `` (``) 提供了一組經典的字符串處理函數,雖然現代 C++ 推薦使用 `std::string`/`std::wstring`,但理解這些底層函數對於維護舊代碼、嵌入式開發、高性能解析、與 C 庫互操作至關重要。本文系統整理 5 大核心函數族,詳細解析參數、返回值、緩衝區安全風險、現代安全替代品,並提供完整可編譯範例。

核心函數族速查表

功能標準函數 (不安全)安全替代 (C11 Annex K / MSVC)關鍵風險
複製strcpy(dest, src)strcpy_s(dest, destsz, src) / strncpy_s緩衝區溢出 (dest 過小)
複製 N 字符strncpy(dest, src, n)strncpy_s(dest, destsz, src, n)不保證以 \0 結尾 (src≥n)
追加strcat(dest, src)strcat_s(dest, destsz, src)緩衝區溢出
追加 N 字符strncat(dest, src, n)strncat_s(dest, destsz, src, n)緩衝區溢出
比較strcmp(s1, s2)無 (本身安全)無
比較 N 字符strncmp(s1, s2, n)無無
分詞strtok(str, delim)strtok_s(str, delim, context)非線程安全、修改原字符串
長度strlen(s)無無 (需確保以 \0 結尾)

函數詳細解析與安全用法

1. strcpy / strcpy_s — 字符串複製


// 不安全版
char *strcpy(char *dest, const char *src);
// 安全版 (C11/MSVC)
errno_t strcpy_s(char *dest, rsize_t destsz, const char *src);

// 關鍵點:
// - dest 必須有足夠空間容納 src + \0
// - strcpy_s 會檢查 destsz,失敗返回錯誤碼並將 dest[0] = \0
// - 返回 0 表示成功

2. strncpy / strncpy_s — 指定長度複製


// 不安全版
char *strncpy(char *dest, const char *src, size_t n);
// 安全版
errno_t strncpy_s(char *dest, rsize_t destsz, const char *src, size_t n);

// 陷阱:若 src 長度 ≥ n,dest **不會** 自動添加 \0 結尾!
// 安全模式:手動確保結尾
strncpy_s(buf, sizeof(buf), src, sizeof(buf)-1);
buf[sizeof(buf)-1] = '\0'; // 強制結尾

3. strcat / strcat_s — 字符串追加


// 不安全版
char *strcat(char *dest, const char *src);
// 安全版
errno_t strcat_s(char *dest, rsize_t destsz, const char *src);

// 必須確保 dest 有足夠剩餘空間
// 現代寫法:snprintf 或 std::string

4. strcmp / strncmp — 字符串比較


int strcmp(const char *s1, const char *s2);       // 字典序比較
int strncmp(const char *s1, const char *s2, size_t n); // 比較前 n 字符

// 返回值:
// < 0 : s1 < s2
// = 0 : s1 == s2
// > 0 : s1 > s2
// 注意:區分大小寫,按 ASCII/字節值比較
// 不區分大小寫:_stricmp / strcasecmp (POSIX)

5. strtok / strtok_s — 字符串分詞 (破壞性、非線程安全)


// 不安全版:內部靜態指針保存狀態,非線程安全,修改原字符串
char *strtok(char *str, const char *delim);
// 安全版:顯式傳遞上下文指針
char *strtok_s(char *str, const char *delim, char **context);

// 用法模式:
char *ctx = NULL;
char *token = strtok_s(str, " ,.;", &ctx); // 首次傳 str
while (token) {
    // 處理 token
    token = strtok_s(NULL, " ,.;", &ctx); // 後續傳 NULL
}
// 現代替代:std::stringstream, std::regex, boost::split, C++20 std::split

6. strlen — 字符串長度


size_t strlen(const char *s);
// 返回 \0 前的字符數 (不含 \0)
// 前提:s 必須以 \0 結尾,否則越界讀取
// 無安全版,因為只讀不寫

完整可編譯範例 (Visual C++ / GCC / Clang 通用)


#include 
#include 
#include 

int main() {
    // 1. strcpy / strncpy
    char src[] = "Hello, World!";
    char dest1[50];
    char dest2[10]; // 故意較小

    strcpy(dest1, src); // 安全:dest1 足夠大
    printf("strcpy: %s\n", dest1);

    // strncpy 安全用法
    strncpy(dest2, src, sizeof(dest2)-1);
    dest2[sizeof(dest2)-1] = '\0'; // 強制結尾
    printf("strncpy (truncated): %s\n", dest2);

    // 2. strcat / strncat
    char base[50] = "Prefix: ";
    strcat(base, src);
    printf("strcat: %s\n", base);

    char base2[20] = "Prefix: ";
    strncat(base2, src, sizeof(base2) - strlen(base2) - 1);
    printf("strncat: %s\n", base2);

    // 3. strcmp / strncmp
    printf("strcmp('abc', 'abd'): %d\n", strcmp("abc", "abd")); // -1
    printf("strncmp('abc', 'abd', 2): %d\n", strncmp("abc", "abd", 2)); // 0 (前2字符相同)

    // 4. strtok_s 分詞
    char sentence[] = "This is a sentence with 7 tokens";
    char *ctx = NULL;
    char *token = strtok_s(sentence, " ", &ctx);
    printf("Tokens:\n");
    while (token) {
        printf("  [%s]\n", token);
        token = strtok_s(NULL, " ", &ctx);
    }
    // 注意:sentence 已被修改 (分隔符替換為 \0)

    // 5. strlen
    printf("strlen(src): %zu\n", strlen(src));

    return 0;
}

現代 C++ 最佳實踐:拋棄 C 字符串,擁抱 std::string


#include 
#include 
#include  // C++17
#include 

// 複製/賦值/追加/比較/長度 全部安全、直觀、無緩衝區風險
std::string s1 = "Hello";
std::string s2 = s1 + ", World!"; // 追加
if (s1 == "Hello") {} // 比較
size_t len = s1.size(); // 長度

// 分詞 (C++20 std::split 或範圍庫)
std::string_view sv = "a,b,c";
for (auto token : sv | std::views::split(',')) {
    std::string_view part(token.begin(), token.end());
    // 處理 part
}

// 舊 API 互操作:c_str() (只讀), data() (C++17 保證 \0 結尾)
// 傳給 C API: legacy_func(s1.c_str());
// 接收 C API 返回: std::string result(c_api_return_ptr); // 假設以 \0 結尾

總結

C 標準庫字符串函數 (strcpy, strcat, strcmp, strtok, strlen) 是系統編程基石,但**緩衝區溢出、非線程安全、修改原串**等缺陷導致無數安全漏洞。**現代 C/C++ 開發鐵律**:

  1. **業務邏輯全面使用 `std::string`/`std::wstring`/`std::string_view`**。
  2. **與 C API 交互邊界** 才使用 C 字符串函數,且**必須使用安全版** (_s 後綴 / `strncpy_s` 等) 並嚴格檢查返回碼。
  3. **禁用 `gets`、`sprintf`、`strcpy`、`strcat` 等不安全函數** (編譯器警告視為錯誤)。
  4. 分詞優先用 `std::stringstream`、範圍庫、Boost.Tokenizer,避免 `strtok` 破壞原數據與線程不安全。

掌握 C 字符串函數語義與陷阱,是讀懂舊代碼、編寫高性能解析器、進行安全審計的基本功;但日常開發請堅定選擇 `std::string`。

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