C 语言内存操作函数
更新时间:2026-08-26。本文是
languages/c/主题高手层文档。
本文要回答的问题
memcpy和按字段赋值有什么区别?memmove又解决了什么?- 把结构体
memset成 0 是万能的吗? - "严格别名"规则为什么会坑到人?
一、内存操作函数族
| 函数 | 作用 | 注意 |
|---|---|---|
memcpy(dst, src, n) | 按字节拷贝 n 字节 | 区域不能重叠 |
memmove(dst, src, n) | 拷贝 n 字节 | 允许重叠(内部判断方向) |
memset(p, c, n) | 把 n 字节设为 c | 常用于清零结构体 |
memcmp(a, b, n) | 逐字节比较 | 比 strcmp 快(不遇 0 停) |
memcpy 常被编译器内联成高效指令序列(甚至 SIMD),性能远好于手写循环。重叠区域必须用 memmove,这是最常见的误用点。
两者差别在"重叠时怎么搬"——memcpy 从头往后拷,重叠区会覆盖源数据;memmove 先判断方向、必要时从尾往前拷,保证源不被踩:

图里 memcpy 标了"重叠会踩源"(红,危险),memmove 标了"重叠从尾→头"(绿,安全)——所以"不确定是否重叠就用 memmove"不是玄学,是它内部多做了一次方向判断。
二、字节视角与别名
memset(&obj, 0, sizeof(obj))清零结构体是惯例,但对浮点、指针清零并不保证是"合法空值"(指针通常 OK,但标准只保证整型 0)。- 严格别名规则:不同类型的指针不能指向同一内存做读写(除非
char*)。违反它,-O2下编译器可能按假设优化出错误结果。
c
int x = 5;
float *f = (float *)&x; /* 危险:违反严格别名 */正确做法:用 memcpy 做"类型双关"(type punning),编译器会正确对待。
三、实测:四个函数的行为
c
char src[] = "hello world";
char dst[20];
memcpy(dst, src, strlen(src) + 1); // 拷贝(不重叠)
printf("memcpy: %s\n", dst);
char buf[] = "abcdef";
memmove(buf + 2, buf, 4); // 重叠拷贝(安全)
printf("memmove(重叠): %s\n", buf);
char arr[10];
memset(arr, 'A', sizeof(arr) - 1); // 填充
int r = memcmp("abc", "abd", 3); // 逐字节比较实测输出:
text
memcpy: dst = hello world
memmove(重叠): ababcd ← memmove 正确处理了重叠
memset: AAAAAAAAA
memcmp(abc,abd): -1 ← 负数表示 a < b关键对比:memcpy 遇重叠是未定义行为(可能拷出乱码),memmove 内部判断拷贝方向、正确处理重叠——所以"不确定是否重叠时用 memmove"是安全准则(memmove 只比 memcpy 慢一点点)。
四、与入门层的衔接
- 指针运算与字节地址,见 指针运算。
- 通用字节缓冲区与 void*,见 void 指针。
- 二进制文件读写中内存操作的应用,见 fread/fwrite。
一句话总结
memcpy/memset 是"按字节干活"的底层工具:重叠用 memmove、双关用 memcpy,尊重严格别名规则,优化器才不会在背后坑你。
本文已完成填充:示例代码与实测数据已补齐。