C语言进阶|四大内存函数深度剖析:memcpy/memmove/memset/memcmp(原理+实战+手写模拟+思维导图)
前言
字符串函数依赖\0结束标记,mem系列内存函数直接操作原始内存、按字节处理、无视数据类型、不受结束符限制,是面试笔试必考、结构体/动态内存/数组拷贝刚需。
本文全覆盖:函数原型、使用示例、底层原理、手写源码、重叠坑点、易错汇总,附带结构化思维导图,代码全平台可直接编译,可写入简历项目与课程设计。
头文件统一:
#include <string.h>、#include <assert.h>
一、C语言内存函数完整思维导图
C语言mem四大内存函数知识体系
├─ 基础共性
│ ├─ 头文件:<string.h>,参数通用void*泛型指针
│ ├─ 底层逻辑:强制转char*,单字节操作,size_t无符号字节计数
│ └─ 适用范围:char/int/结构体/动态内存全类型,不受'\0'约束
├─ 内存拷贝类
│ ├─ memcpy:非重叠内存拷贝,源目标重叠→未定义行为UB,正向逐字节拷贝
│ │ ├─ 原型:void* memcpy(void* dest,const void* src,size_t count)
│ │ ├─ 适用:两块独立不重叠数组、结构体拷贝
│ │ └─ 模拟:从前到后依次赋值
│ └─ memmove:兼容内存重叠拷贝,分正向/反向双逻辑
│ ├─ dest>src:从前向后拷贝;dest<src:从后向前拷贝
│ ├─ 重叠场景唯一安全方案
│ └─ 模拟:地址判断分支拷贝
├─ 内存赋值类 memset
│ ├─ 原型:void* memset(void* ptr,int val,size_t num)
│ ├─ 特性:逐字节赋值,val仅截取低8bit
│ ├─ 正确:字符数组清零、填充字符
│ └─ 踩坑:禁止用memset批量赋值int为1(单字节赋值出错)
├─ 内存比较类 memcmp
│ ├─ 原型:int memcmp(const void* p1,const void* p2,size_t count)
│ ├─ 返回:p1>p2>0 / p1==p2=0 / p1<p2<0,逐字节ASCII对比
│ └─ 区别strcmp:不受'\0'截断,固定对比指定字节
├─ 手写模拟实现要点
│ ├─ assert断言拦截空指针
│ ├─ 保存dest首地址用于函数返回
│ ├─ void*强转char*实现单字节遍历
│ └─ memmove做地址大小分支判断
├─ 高频易错踩坑清单
│ ├─ memcpy重叠内存依赖编译器,标准未定义,严禁跨区域重叠拷贝
│ ├─ memset赋值int数组非0值全部失效
│ ├─ size_t无符号,count传负数会超大循环
│ └─ memcmp对比长度写错导致越界访问
└─ 项目实战拓展
├─ 结构体数组批量拷贝
├─ malloc动态内存初始化清零
├─ 自定义通用内存工具头文件mymem.h
二、前置预备知识点
1. void*:泛型空指针,不能直接解引用,必须强转为char*实现单字节访问
2. size_t:无符号整型unsigned int,字节计数,负数入参会隐式转为超大正数3. 内存函数不识别\0,按传入字节数硬性操作,是和str系列最大区别
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <assert.h>
三、memcpy 内存拷贝(非重叠专用)
函数原型
void* memcpy(void* dest, const void* src, size_t count);
• dest:目标地址,可修改;src:源地址const只读;count:字节总数
• 标准规定:源与目标内存不能重叠,重叠结果未定义UB,部分VS编译器兼容重叠是编译器扩展,不可跨平台依赖
库函数实战示例
void test_memcpy()
{
int arr1[] = {1,2,3,4,5};
int arr2[5] = {0};
//拷贝整个数组:元素数×int字节(4)
memcpy(arr2,arr1,sizeof(arr1));
for(int i=0;i<5;i++) printf("%d ",arr2[i]);
//输出:1 2 3 4 5
}
手写模拟my_memcpy
void* my_memcpy(void* dest, const void* src, size_t count)
{
assert(dest && src);
char* ret = (char*)dest; //保存起始地址用于返回
char* pd = (char*)dest;
const char* ps = (const char*)src;
while(count--)
{
*pd++ = *ps++;
}
return ret;
}
四、memmove 安全内存移动(支持任意内存重叠)
原型同memcpy,逻辑分支不同
• 场景1:dest > src → 目标在源后方,从前向后拷贝
• 场景2:dest < src → 目标在源前方,从后向前倒序拷贝,防止未拷贝数据被覆盖
重叠错误演示(memcpy失效、memmove正常)
void test_memmove()
{
int arr[] = {1,2,3,4,5,6,7,8,9};
//把前4个元素拷贝到下标2起始位置(内存重叠)
//memcpy(arr+2,arr,16); //标准未定义,大概率数据错乱
memmove(arr+2,arr,16);
for(int i=0;i<9;i++) printf("%d ",arr[i]);
//输出:1 2 1 2 3 4 7 8 9
}
手写模拟my_memmove
void* my_memmove(void* dest, const void* src, size_t count)
{
assert(dest && src);
char* ret = (char*)dest;
char* pd = (char*)dest;
const char* ps = (const char*)src;
if(pd > ps)
{
//目标在后:倒着拷贝
pd += count - 1;
ps += count - 1;
while(count--) *pd-- = *ps--;
}
else
{
//目标在前:正向拷贝,同memcpy
while(count--) *pd++ = *ps++;
}
return ret;
}
五、memset 内存批量字节赋值
原型
void* memset(void* ptr,int val,size_t num);
• val是int,但只截取低8位单字节写入内存,核心易错点
正确用法:字符数组清零/填充
void test_memset()
{
char str[20] = "abcdef";
memset(str, 'x', 3);
printf("%s\n",str); //xxxdef
}
经典错误:int数组赋值1(禁用)
int arr[5];
memset(arr,1,sizeof(arr));
//错误:单字节赋值0x01 → int=0x01010101≠1
//仅memset(arr,0,sizeof(arr))可以实现整型数组清零
简易模拟实现
void* my_memset(void* ptr,int val,size_t num)
{
assert(ptr);
char* p = (char*)ptr;
unsigned char ch = (unsigned char)val; //截取低8位
while(num--) *p++ = ch;
return ptr;
}
六、memcmp 内存逐字节比较
原型
int memcmp(const void* p1,const void* p2,size_t count);
• 逐字节对比ASCII,对比满count字节才结束,不受\0截断(和strcmp核心区别)
• 返回:>0 / =0 / <0
void test_memcmp()
{
char s1[] = "abc123";
char s2[] = "abc999";
int ret = memcmp(s1,s2,3); //前3字节abc相等→0
printf("%d",ret);
}
手写模拟my_memcmp
int my_memcmp(const void* p1,const void* p2,size_t count)
{
assert(p1 && p2);
const char* ps1 = (const char*)p1;
const char* ps2 = (const char*)p2;
while(count--)
{
if(*ps1 != *ps2)
return *ps1 - *ps2;
ps1++,ps2++;
}
return 0;
}
七、汇总测试main函数
int main()
{
test_memcpy();
printf("\n");
test_memmove();
printf("\n");
test_memset();
printf("\n");
test_memcmp();
return 0;
八、四大函数高频踩坑(面试高频提问)
1. memcpy重叠不可信:C标准UB,仅memmove适配重叠拷贝
2. memset整型陷阱:仅能清零int数组,无法批量置1/5等非0值
3. size_t无符号坑:count传入负数自动转为超大无符号数,越界循环
4. memcmp长度参数:必须精确计算字节数,多写少写都会逻辑错误
5. void*不能自增解引用:必须强转char*才能单字节遍历
九、实战拓展(课设加分方向)
1. 结构体数组整块内存拷贝(最常用项目场景)
struct Stu{char name[10];int age;};
struct Stu s1[2],s2[2];
memcpy(s2,s1,sizeof(s1));
2. malloc动态开辟内存后用memset初始化清零
3. 封装mymem.h自定义内存工具头文件
十、专栏下期预告
C语言动态内存管理:malloc/calloc/realloc/free+柔性数组深度解析
点赞收藏,源码可直接复制到VS/GCC一键运行!
更多推荐


所有评论(0)