前言

字符串函数依赖\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一键运行!

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐