C语言内存函数详解:memcpy、memmove、memset、memcmp 面试复习指南
·
📖 目录
- 📚 1. 引言
- 📦 2. memcpy 函数:内存拷贝的基础
- 🔄 3. memmove 函数:处理重叠内存的拷贝
- 🧹 4. memset 函数:内存设置
- ⚖️ 5. memcmp 函数:内存比较
- 📝 6. 总结
- 🎯 7. 经典面试题
📚 1. 引言
大家好,欢迎来到C语言内存函数的专题讲解。在C语言编程中,内存操作是极其重要的一环,尤其是在嵌入式开发、系统编程和面试中,memcpy、memmove、memset 和 memcmp 这四个内存函数几乎是必考知识点。
本文将带你从函数原型、使用示例、模拟实现到面试常见问题,全方位掌握这些内存函数。文章内容基于经典教材和实际开发经验,代码完整可运行,非常适合面试前的系统复习。
📦 2. memcpy 函数:内存拷贝的基础
2.1 函数原型
void * memcpy ( void * destination, const void * source, size_t num );
2.2 功能说明
memcpy从source位置开始,向后复制num个字节的数据到destination指向的内存位置。- 这个函数在遇到
'\0'的时候并不会停下来,它只关心num指定的字节数。 - ⚠️ 重要限制:如果
source和destination有任何的重叠,复制的结果都是未定义的。对于重叠的内存,应该交给memmove来处理。
2.3 使用示例
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
// 将 arr1 的前 20 个字节(5个int)复制到 arr2
memcpy(arr2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
// 输出结果:1 2 3 4 5 0 0 0 0 0
return 0;
}
2.4 模拟实现
void * memcpy ( void * dst, const void * src, size_t count)
{
void * ret = dst;
assert(dst);
assert(src);
/*
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return(ret);
}
💡 实现要点:
- 使用
char *指针逐字节拷贝,因为char类型占1个字节,可以精确控制拷贝的字节数。- 先保存
dst的原始地址作为返回值,方便链式调用。- 使用
assert进行空指针检查,增强代码健壮性。
🔄 3. memmove 函数:处理重叠内存的拷贝
3.1 函数原型
void * memmove ( void * destination, const void * source, size_t num );
3.2 功能说明
- 和
memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。 - 如果源空间和目标空间出现重叠,就得使用
memmove函数处理。
3.3 使用示例
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
// 将 arr1 的前 20 个字节复制到 arr1+2 的位置(重叠场景)
memmove(arr1+2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
// 输出结果:1 2 1 2 3 4 5 8 9 10
return 0;
}
输出结果:
1 2 1 2 3 4 5 8 9 10
3.4 模拟实现
void * memmove ( void * dst, const void * src, size_t count)
{
void * ret = dst;
if (dst <= src || (char *)dst >= ((char *)src + count)) {
/*
* Non-Overlapping Buffers
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}
else {
/*
* Overlapping Buffers
* copy from higher addresses to lower addresses
*/
dst = (char *)dst + count - 1;
src = (char *)src + count - 1;
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return(ret);
}
💡 实现要点:
- 核心思想是判断内存是否重叠,以及从哪个方向开始拷贝。
- 当
dst <= src或dst >= src + count时,说明没有重叠或目标地址在源地址之前,可以从低地址向高地址拷贝。- 当存在重叠且目标地址在源地址之后时,必须从高地址向低地址拷贝,避免覆盖未拷贝的数据。
🧹 4. memset 函数:内存设置
4.1 函数原型
void * memset ( void * ptr, int value, size_t num );
4.2 功能说明
memset 是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。
4.3 使用示例
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "hello world";
memset (str,'x',6);
printf(str);
// 输出结果:xxxxxxworld
return 0;
}
输出结果:
xxxxxxworld
💡 注意事项:
memset以字节为单位操作,对于非字符数组(如int数组),设置的值会被解释为字节值。- 例如
memset(arr, 1, 20)会将每个字节设为0x01,而不是将每个int设为1。- 常用于初始化数组为0:
memset(arr, 0, sizeof(arr))。
⚖️ 5. memcmp 函数:内存比较
5.1 函数原型
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
5.2 功能说明
- 比较从
ptr1和ptr2指针指向的位置开始,向后的num个字节。 - 返回值:
- 返回值
< 0:ptr1小于ptr2 - 返回值
= 0:ptr1等于ptr2 - 返回值
> 0:ptr1大于ptr2
- 返回值
5.3 使用示例
#include <stdio.h>
#include <string.h>
int main()
{
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n;
n = memcmp(buffer1, buffer2, sizeof(buffer1));
if (n > 0)
printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
else if (n < 0)
printf("'%s' is less than '%s'.\n", buffer1, buffer2);
else
printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
return 0;
}
💡 与
strcmp的区别:
strcmp遇到'\0'就停止比较,而memcmp比较指定的num个字节。memcmp可以比较任意内存块(包括包含'\0'的数据),而strcmp只能比较字符串。
📝 6. 总结
| 函数 | 功能 | 处理重叠内存 | 停止条件 |
|---|---|---|---|
memcpy |
内存拷贝 | ❌ 未定义 | 指定字节数 |
memmove |
内存拷贝 | ✅ 安全处理 | 指定字节数 |
memset |
内存设置 | — | 指定字节数 |
memcmp |
内存比较 | — | 指定字节数 |
核心记忆口诀:
memcpy:不重叠,直接拷memmove:重叠了,分方向memset:设内存,按字节memcmp:比内存,定字节
🎯 7. 经典面试题
面试题 1:memcpy 和 memmove 有什么区别?什么时候用 memmove?
解答:
memcpy不保证能正确处理源和目标内存重叠的情况,如果内存重叠,结果是未定义的。memmove可以安全处理重叠内存。- 当源内存块和目标内存块有重叠时,必须使用
memmove;否则两者都可以使用,但memcpy通常效率略高。
面试题 2:memcpy 和 strcpy 有什么区别?
解答:
strcpy只能用于字符串拷贝,遇到'\0'停止;memcpy可以拷贝任意类型的内存数据。strcpy不指定拷贝长度;memcpy需要指定拷贝的字节数。memcpy可以拷贝包含'\0'的数据块,而strcpy遇到'\0'就停止。
面试题 3:memset(arr, 1, sizeof(arr)) 能把 int 数组的每个元素设为 1 吗?
解答:
- 不能。
memset以字节为单位设置内存,int占4个字节,每个字节都会被设为0x01,所以每个int的值实际上是0x01010101(十六进制),即16843009。 - 正确做法是使用循环或
memset(arr, 0, sizeof(arr))来清零数组。
面试题 4:请手写一个 memmove 的实现,并说明为什么需要分方向拷贝?
解答:
- 参考上文第3.4节的模拟实现。
- 分方向拷贝的原因:当源地址和目标地址重叠,且目标地址在源地址之后时,如果从低地址向高地址拷贝,会先覆盖源数据中尚未拷贝的部分,导致数据错误。从高地址向低地址拷贝可以避免这个问题。
面试题 5:memcmp 和 strcmp 的返回值规则一样吗?比较结果有什么不同?
解答:
- 返回值规则相同:都返回
>0、=0、<0。 - 不同点:
strcmp遇到'\0'停止比较,而memcmp比较完指定的num个字节才停止。因此对于包含'\0'的二进制数据,只能用memcmp进行比较。
🚀 面试小贴士:面试官通常会让手写
memcpy或memmove的模拟实现,重点考察对内存重叠的处理、指针操作和边界条件的把握。建议在面试前多练习几遍手写代码,做到烂熟于心。
更多推荐



所有评论(0)