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

C語言 memset 完全指南:語法、逐字節填充特性、三大常見錯誤與最佳實踐

     編輯:關於C語言

函數原型與核心語義

#include 
void *memset(void *ptr, int value, size_t num);
  • 功能: 從 ptr 開始,將連續 num **字節** 設置為 value (取低 8 位)。
  • 返回: ptr (鏈式調用)。
  • 核心特性: **逐字節填充** — 不關心數據類型,只操作原始字節。

典型用法:清零結構體/數組 (最快、最標準)

struct Config cfg;
memset(&cfg, 0, sizeof(cfg)); // 一鍵歸零,含嵌套結構體、數組、指針

int arr[100];
memset(arr, 0, sizeof(arr)); // 等價 arr 初始化為 {0},但運行期執行

三大常見錯誤 (必讀避坑)

❌ 錯誤 1:參數順序顛倒 — memset(ptr, num, value)

// 正確:memset(目標, 填充值, 字節數)
memset(buffer, 0, 20);   // 清零 20 字節
// 錯誤:memset(buffer, 20, 0); // 填充值 20,長度 0 → 無操作,極難發現

記憶口訣: 「目標、值、長」 — memset(where, what, how_many_bytes)。

❌ 錯誤 2:對非字符類型填充非 0/-1 值

int arr[5];
memset(arr, 1, sizeof(arr)); // 每字節 = 0x01
// arr[0] = 0x01010101 (16843009) ≠ 1 !
// arr[1] = 0x01010101 ...
// 只有 0 (全 0) 和 -1 (全 1, 即 0xFF) 適合逐字節填充產生預期整數值
memset(arr, -1, sizeof(arr)); // 每字節 0xFF → int = -1 (補碼全 1)
memset(arr, 0, sizeof(arr));  // 每字節 0x00 → int = 0

結論: memset 只能可靠地將整型/浮點數組設置為 **0** 或 **-1** (對應補碼全 0/全 1)。其他值請用循環賦值或 std::fill (C++)。

❌ 錯誤 3:對指針/結構體指針用 sizeof(指針) 而非 sizeof(對象)

struct Node *p = malloc(sizeof(struct Node));
// 錯誤:memset(p, 0, sizeof(p)); // sizeof(p) = 4/8 (指針大小) → 僅清零指針變量本身,不清零分配的內存!
// 正確:
memset(p, 0, sizeof(*p));       // sizeof(*p) = sizeof(struct Node)
memset(p, 0, sizeof(struct Node)); // 同理

進階陷阱與注意事項

  1. 浮點數清零陷阱: IEEE-754 中全 0 位模式表示 +0.0,**通常安全**,但某些平台非標準浮點格式可能異常。嚴格移植用循環 arr[i] = 0.0。
  2. 指針清零陷阱: 全 0 位模式在絕大多數平台表示空指針 (NULL),但 C 標準**不保證**。極少數架構 (如某些 DSP、段式架構) NULL 非全 0。可移植代碼應顯式賦 NULL / nullptr。
  3. 虛函數表指針 (C++): memset(this, 0, sizeof(*this)) 會破壞 vptr,導致虛函數調用崩潰。**C++ 類對象嚴禁 memset**。
  4. 重疊內存: memset 不處理源目重疊 (它沒有源),但若 ptr 範圍覆蓋自身關鍵元數據 (如容器的 size/capacity 字段),會導致邏輯錯誤。
  5. 性能: 編譯器常優化 memset(buf, 0, n) 為向量化指令 (SSE/AVX/NEON) 或 rep stosb,極快。**大塊內存清零首選 memset**。

對比:memset vs 初始化列表 vs 循環賦值

場景推薦方式理由
結構體/數組定義時清零Type obj = {0}; / Type obj{}; (C++11)編譯期/靜態初始化,最安全、無運行開銷
運行期重置大型結構體/緩衝區memset(&obj, 0, sizeof(obj))極快,逐字節零填充
設置為特定非零值 (整型)循環賦值 / std::fill_n (C++)memset 逐字節語義導致值錯誤
浮點數組初始化循環 / std::fill / std::vector(n, 0.0)避免非標準浮點表示問題
C++ 類對象 (含 vptr/非 POD)構造函數 / obj = Type{} / std::fillmemset 破壞對象語義

實戰代碼模板

// 1. 安全清零宏 (C)
#define ZERO_OBJ(obj) memset(&(obj), 0, sizeof(obj))

// 使用
struct Config cfg;
ZERO_OBJ(cfg);

// 2. 分配並清零 (C)
void *ptr = malloc(size);
if (ptr) memset(ptr, 0, size);

// 3. C++ 現代替代 (針對 POD 類型)
#include 
#include 
std::fill(std::begin(arr), std::end(arr), 0); // 類型安全,可內聯優化
std::memset(arr, 0, sizeof(arr));             // 仍可用,但需確保 POD

總結

  1. memset 是逐字節操作 — 只認字節,不認類型。
  2. 參數順序:目標、值、字節數 — 寫反是隱形 Bug 之王。
  3. 只能可靠填充 0 和 -1 (對整型) — 其他值用循環。
  4. sizeof 參數必須是對象大小,非指針大小 — sizeof(*ptr)。
  5. C++ 非 POD 類型 (含虛函數、std::string、智能指針) 絕對禁用 memset — 破壞對象不變量,導致 UB。
  6. 定義時初始化用 {0}/{},運行期大塊清零用 memset,非零填充用循環/算法。

牢記「逐字節、順序、0/-1、對象大小、C++禁用」六字訣,memset 就是你手中最高效、最安全的內存清零利器。

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