一、函数原型
#include <string.h> void *memset(void *s, int c, size_t n);
| 参数 | 含义 |
|---|---|
s | 要操作的内存起始地址 |
c | 要设置的值(只取低8位,0-255) |
n | 要设置的字节数 |
返回值:s本身(方便链式调用)
二、最常用场景:清零
1. 清零数组
int arr[100]; memset(arr, 0, sizeof(arr));
2. 清零结构体
typedef struct { int id; char name[20]; float score; } Student; Student stu; memset(&stu, 0, sizeof(Student));3. 清零字符串缓冲区
char buffer[256]; memset(buffer, 0, sizeof(buffer));
三、填充非零值(仅限字节类型)
1. 填充字符数组 ✅
char str[100]; memset(str, 'A', 50); // 前50个字节填 'A' memset(str + 50, 'B', 50); // 后50个字节填 'B'
2. 填充整数数组 ❌ 错误示
int arr[10]; memset(arr, 1, sizeof(arr)); // 想全部设为1? // 实际结果:arr[0] = 0x01010101 = 16843009
原因:memset按字节填充,int占4个字节,每个字节都变成0x01,组合起来就是0x01010101。
3. 填充指针数组 ⚠️ 危险
char *ptrs[10]; memset(ptrs, 0, sizeof(ptrs)); // 清零指针 → NULL,安全 memset(ptrs, 1, sizeof(ptrs)); // 危险!得到无效地址
四、结构体内存对齐的影响
typedef struct { char c; // 1 字节 int x; // 4 字节(编译器会填充3个空字节) } Test; Test t; memset(&t, 0, sizeof(Test)); // 连填充字节一起清零,完全安全
memset是整个字节逐字节填充,对齐的填充字节也会被覆盖,但这是好事——防止垃圾数据。
五、= {0}vsmemset对比
| 场景 | = {0} | memset |
|---|---|---|
| 定义局部变量 | ✅ 简洁 | 可以但多余 |
| 全局变量 | ✅ 默认就是0 | 多余 |
| 变量已存在需重新清零 | ❌ 不行 | ✅ 必须 |
| 动态内存 | ❌ 不行 | ✅ 必须 |
| 部分清零 | ❌ 不行 | ✅ 必须 |
| 填充非零字符 | ❌ 不行 | ✅ 必须 |
示例:重新清零
int arr[100]; // 使用 arr... // 想全部清零 memset(arr, 0, sizeof(arr)); // 只能这样
示例:动态内存
int *p = malloc(100 * sizeof(int)); memset(p, 0, 100 * sizeof(int)); // 或用 calloc(100, sizeof(int)) 一步到位
示例:部分清零
struct Data { int header; char payload[256]; int crc; } d; // 只清零 payload memset(d.payload, 0, sizeof(d.payload));六、实用技巧
技巧1:安全计算字节数
int arr[50]; memset(arr, 0, sizeof(arr)); // ✅ 推荐 memset(arr, 0, 50 * sizeof(int)); // ✅ 也可以 memset(arr, 0, 50); // ❌ 只清了50字节(应200字节)
技巧2:链式调用
char buf[256]; memset(buf, 0, sizeof(buf)); strcpy(buf, "hello"); // 分开写 // 链式(但不推荐,可读性差) strcpy((char*)memset(buf, 0, sizeof(buf)), "hello");
技巧3:配合memcpy用
char dest[100]; char src[] = "world"; memset(dest, 0, sizeof(dest)); // 先清零 memcpy(dest, src, sizeof(src) - 1); // 再拷贝(-1 去掉src的'\0') // dest[5] 已经是 '\0',安全输出字符串
技巧4:复位环形缓冲区
#define BUF_SIZE 1024 char ring_buf[BUF_SIZE]; int write_idx = 0, read_idx = 0; void reset_buffer(void) { memset(ring_buf, 0, BUF_SIZE); write_idx = 0; read_idx = 0; }七、常见错误速查
| 错误写法 | 后果 | 正确写法 |
|---|---|---|
memset(arr, 1, sizeof(arr)) | 整数值变0x01010101 | 用 for 循环 |
memset(arr, 0, 50)数组长100 | 只清零一半 | memset(arr, 0, sizeof(arr)) |
memset(&arr, 0, sizeof(arr)) | &arr多余但合法 | memset(arr, 0, sizeof(arr)) |
memset(p, 0, sizeof(p))p 是指针 | 只清4/8字节 | memset(p, 0, size)传真实大小 |
八、性能说明
// 现代编译器会将 memset 优化为极高效的指令 memset(arr, 0, 1000); // 可能变成 rep stosd(一次写4字节) // 手写循环反而更慢 for (int i = 0; i < 1000; i++) arr[i] = 0;
放心用
memset,它比大多数手写循环快。
九、快速记忆卡片
| 你想做什么 | 代码 |
|---|---|
| 清零数组 | memset(arr, 0, sizeof(arr)) |
| 清零结构体 | memset(&stu, 0, sizeof(stu)) |
| 清零字符串 | memset(str, 0, sizeof(str)) |
| 填充字符 A | memset(buf, 'A', 100) |
| 填充整数 1 | ❌ 不行,用 for 循环 |
| 部分清零 | memset(&obj.member, 0, sizeof(obj.member)) |
| 动态内存清零 | memset(ptr, 0, size)或直接用calloc |
十、一句话总结
memset是按字节填充的万能清零工具,最适合清零整块内存(数组、结构体、缓冲区)。定义时能用= {0}就用,但变量已存在、动态内存、部分清零时只能靠memset。