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

C語言 memset/memcpy 陷阱:size_t 無符號特性與緩衝區邊界錯誤

     編輯:關於C

memset、memcpy 等內存操作函數是 C 程序員的「家常便飯」,但其參數類型 size_t(無符號)與指針算術的微妙交互,極易導致驚人的邊界錯誤。本文深度剖析三大經典陷阱。

陷阱一:sizeof 不是函數,易誤用於指針

int a[100];
int *b = a;
sizeof(a)  // 400 (數組大小)
sizeof(b)  // 4/8 (指針大小),非數組大小!

結論:sizeof 是編譯期運算符,僅對類型或數組表達式返回對象大小;對指針變量返回指針本身大小。函數參數中的數組退化為指針,sizeof 失效。

陷阱二:memset 第三參數單位是字節,非元素個數

int a[100];
memset(a, 0, 100); // 錯誤!僅清零前 100 字節(25 個 int)
memset(a, 0, sizeof(a)); // 正確:400 字節
memset(a, 0, 100 * sizeof(int)); // 正確

原因:void *memset(void *s, int c, size_t n) 的 n 是字節數。傳入元素個數會導致部分內存未初始化。

陷阱三:memcpy 實現需處理重疊、空指針、負長度

面試必考:手寫 memcpy。

void *my_memcpy(void *dest, const void *src, size_t n) {
    if (!dest || !src) return NULL; // 空指針防護
    // 負長度:size_t 無符號,傳入 -1 會變巨大正數,調用者責任保證 n 合法
    // 重疊:標準 memcpy 不保證重疊正確,需用 memmove
    unsigned char *d = (unsigned char*)dest;
    const unsigned char *s = (const unsigned char*)src;
    while (n--) *d++ = *s++;
    return dest;
}

關鍵點:

  • size_t 無符號,n-- 循環條件 while (n--) 正確處理 0。
  • 逐字節拷貝最安全,無對齊假設。
  • 重疊內存必須用 memmove(反向拷貝或臨時緩衝區)。

進階陷阱:結構體填充導致 memset 初始化失效

struct S { char c; int i; }; // 可能佈局:c + 3字節填充 + i
struct S s;
memset(&s, 0, sizeof(s)); // 看似正確,但...
// 若結構體含浮點數,全 0 位模式不一定是 0.0(雖 IEEE 754 是)
// 若含指針,全 0 不一定是 NULL(極少數架構)
// 更安全:s = (struct S){0}; // C99 指定初始化器

總結檢查清單

  1. memset/memcpy/memmove 第三參數**永遠用 sizeof(對象) 或 元素數 * sizeof(類型)**,別寫死數字。
  2. 函數參數接收數組時,**不能用 sizeof 獲取數組長度**,需額外傳長度參數。
  3. 重疊內存**必須用 memmove**。
  4. 結構體初始化優先用複合字面量 (Type){0} 或指定初始化器,避免填充字節/浮點數/指針表示陷阱。
  5. 啟用編譯器警告 -Wsizeof-pointer-memaccess (GCC/Clang) 自動檢測 sizeof(指針) 誤用。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved