首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

C 語言字串拼接函數 conj 代碼審查:緩衝區溢出隱患與正確寫法

     編輯:程式設計疑難解答

原始考題(填空)

char *conj(char *s, char *t)
{
    char *p = s;
    while (*s) ________;
    while (*t) {
        *s = ________;
        s++;
        t++;
    }
    *s = '\0';
    ________;
}

標準答案:s++, *t, return p。

代碼審查:三大致命缺陷

  1. 修改原字串 s 且無容量檢查 → 緩衝區溢出風險極高。呼叫端若傳入字串常量(conj("hello", "world"))或容量不足的陣列,直接導致未定義行為(崩潰、資料損毀、安全漏洞)。
  2. 無回傳錯誤碼機制,呼叫端無法得知拼接是否成功、是否截斷。
  3. 參數 const 正確性缺失:t 僅讀取,應宣告為 const char *t。

健壯版實作(C99)

#include <stddef.h>  // size_t

/**
 * 安全拼接:將 src 附加到 dst 末尾,確保以 '\0' 結尾。
 * @param dst  目標緩衝區,必須可寫,大小至少 dst_size
 * @param dst_size  目標緩衝區總大小(含結尾 '\0' 空間)
 * @param src  來源字串
 * @return 拼接後字串長度(不含 '\0'),若截斷回傳 -1
 */
int str_concat_safe(char *dst, size_t dst_size, const char *src)
{
    if (!dst || !src || dst_size == 0) return -1;

    size_t dst_len = 0;
    while (dst_len < dst_size && dst[dst_len]) ++dst_len;  // 尋找 dst 結尾

    if (dst_len == dst_size) return -1;  // dst 未以 '\0' 結尾或已滿

    size_t copied = 0;
    while (copied + 1 < dst_size - dst_len && src[copied]) {
        dst[dst_len + copied] = src[copied];
        ++copied;
    }
    dst[dst_len + copied] = '\0';

    return (src[copied] == '\0') ? (int)(dst_len + copied) : -1;  // -1 表示截斷
}

呼叫範例

char buf[100] = "Hello";
str_concat_safe(buf, sizeof buf, ", ");      // 成功
str_concat_safe(buf, sizeof buf, "World!");  // 成功
printf("%s\n", buf);  // Hello, World!

標準庫替代方案

  • strncat(dst, src, dst_size - strlen(dst) - 1) —— 需自行計算剩餘空間,易出現 off-by-one。
  • strlcat(dst, src, dst_size)(BSD/GNU 擴充,非標準) —— 回傳嘗試建立的總長度,截斷檢測方便。
  • snprintf(dst + len, dst_size - len, "%s", src) —— 通用但效能稍低。

教訓總結

「果真是不負責任的考試題。這個函數直接修改了 s 字串不說,還隨意擴充了 s 的使用記憶體,很容易導致緩衝區溢出。」

  • 考試填空題往往簡化錯誤處理,**實戰代碼必須檢查緩衝區邊界**。
  • C 語言無內建字串型別,**字串操作安全性完全由程式設計師負責**。
  • 養成「先問容量、再寫資料」習慣,能消除 90% 的字串漏洞。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved