邢台市网站建设_网站建设公司_jQuery_seo优化
2026/1/16 5:04:31 网站建设 项目流程

🏠个人主页:黎雁
🎬作者简介:C/C++/JAVA后端开发学习者
❄️个人专栏:C语言、数据结构(C语言)、EasyX、游戏、规划
✨ 从来绝巘须孤往,万里同尘即玉京

文章目录

    • 前景回顾:字符串函数核心速记 📝
    • 一、memcpy:不重叠内存的拷贝神器 📤
      • 1. 函数核心要点(<string.h>)
      • 2. 实战示例:拷贝整型数组
      • 3. 模拟实现:逐字节拷贝
      • 4. 注意事项:重叠内存的坑 ❗
    • 二、memmove:处理重叠内存的升级版 🔄
      • 1. 函数核心要点(<string.h>)
      • 2. 核心思路:分情况选择拷贝方向
      • 3. 模拟实现:双向拷贝逻辑
    • 三、memset:按字节设置内存值 🎨
      • 1. 函数核心要点(<string.h>)
      • 2. 正确用法:设置字符数组
      • 3. 经典踩坑:设置非字符类型数组 ❌
    • 四、memcmp:按字节比较内存区域 🆚
      • 1. 函数核心要点(<string.h>)
      • 2. 实战示例:比较整型数组
    • 写在最后 📝

字符串专题收官之后,我们迎来C语言内存操作的核心内容!这一篇聚焦四大内存函数——memcpy(内存拷贝)、memmove(内存移动)、memset(内存设置)、memcmp(内存比较),详解它们的使用规则、核心差异和模拟实现逻辑,帮你吃透以字节为单位的内存操作底层原理!

前景回顾:字符串函数核心速记 📝

C 语言字符串入门:字符函数 + strlen 精讲(从使用到模拟实现)
C 语言字符串进阶:strcpy/strcat/strcmp 精讲
C 语言字符串高阶:strstr/strtok/strerror 精讲(含 strstr 模拟实现)

回顾字符串函数的核心特性,能帮我们更好理解内存函数的设计逻辑:

  1. 字符串函数依赖\0作为终止标志,仅能处理字符类型数据。
  2. 内存函数不关心数据类型,以字节为单位操作内存,通用性更强。
  3. 模拟实现库函数时,assert断言指针非空是保证代码健壮性的必备操作。

一、memcpy:不重叠内存的拷贝神器 📤

memcpy的全称是memory copy,作用是从源内存地址拷贝指定字节数的数据到目标内存地址,和字符串函数strcpy不同,它不会因为遇到\0而停止

1. 函数核心要点(<string.h>)

  • 函数原型
    void*memcpy(void*destination,constvoid*source,size_tnum);
    • destination:目标内存起始地址
    • source:源内存起始地址
    • num:需要拷贝的字节数
  • 核心特性
    ✅ 不关心内存中存储的数据类型,逐字节拷贝
    ✅ 遇到\0不会停止,严格按照num指定的字节数拷贝
    仅适用于不重叠的两块内存区域

2. 实战示例:拷贝整型数组

#include<stdio.h>#include<string.h>intmain(){intarr1[]={1,2,3,4,5};// 占 5*4=20 字节intarr2[10]={0};// 拷贝arr1的20个字节到arr2memcpy(arr2,arr1,20);// 打印arr2:1 2 3 4 5 0 0 0 0 0for(inti=0;i<10;i++)printf("%d ",arr2[i]);return0;}

3. 模拟实现:逐字节拷贝

memcpy的模拟实现核心是强转为char*类型,因为char*每次操作正好是1个字节,完美匹配内存函数的设计逻辑。

#include<stdio.h>#include<assert.h>void*my_memcpy(void*dest,constvoid*src,size_tnum){assert(dest&&src);// 断言指针非空void*ret=dest;// 保存目标起始地址,用于返回// 逐字节拷贝while(num--){*(char*)dest=*(char*)src;src=(char*)src+1;dest=(char*)dest+1;}returnret;}// 测试代码intmain(){intarr1[]={1,2,3,4,5,6,7,8,9,10};intarr2[10]={0};my_memcpy(arr2,arr1,20);// 拷贝前5个整型元素for(inti=0;i<10;i++)printf("%d ",arr2[i]);return0;}

4. 注意事项:重叠内存的坑 ❗

如果用my_memcpy处理重叠内存,会出现数据覆盖问题:

// 错误案例:拷贝重叠区域intarr[]={1,2,3,4,5,6,7,8,9,10};// 想把 {3,4,5,6,7} 拷贝到 {1,2,3,4,5} 的位置my_memcpy(arr,arr+2,20);// 实际输出:{3,4,3,4,3,6,7,8,9,10} → 数据被覆盖

原因是my_memcpy从前往后拷贝,先拷贝的内容会覆盖后面待拷贝的数据。

💡 补充:VS2022等编译器对memcpy做了优化,能处理重叠内存,但标准C规定memcpy只负责不重叠内存,跨平台开发时要严格遵守标准,重叠内存请用memmove

二、memmove:处理重叠内存的升级版 🔄

memmove的全称是memory move,它是memcpy的增强版,支持重叠内存区域的拷贝,是实际开发中更安全的选择。

1. 函数核心要点(<string.h>)

  • 函数原型
    void*memmove(void*destination,constvoid*source,size_tnum);
    参数含义和memcpy完全一致。
  • 核心优势
    ✅ 兼容重叠内存和不重叠内存的拷贝
    ✅ 底层通过判断拷贝方向(从前往后/从后往前)避免数据覆盖

2. 核心思路:分情况选择拷贝方向

我们以数组{1,2,3,4,5,6,7,8,9,10}为例,分析destsrc的位置关系,确定拷贝方向:

内存位置关系拷贝方向原因
dest <= src(目标在源左边)从前往后先拷贝的内容不会覆盖待拷贝数据
dest > src(目标在源右边)从后往前避免先拷贝的内容覆盖后面的数据

3. 模拟实现:双向拷贝逻辑

#include<stdio.h>#include<assert.h>void*my_memmove(void*dest,constvoid*src,size_tnum){assert(dest&&src);void*ret=dest;if(dest<=src)// 情况1:目标在源左边 → 从前往后拷贝{while(num--){*(char*)dest=*(char*)src;src=(char*)src+1;dest=(char*)dest+1;}}else// 情况2:目标在源右边 → 从后往前拷贝{while(num--){// 从最后一个字节开始拷贝*((char*)dest+num)=*((char*)src+num);}}returnret;}// 测试重叠内存拷贝intmain(){intarr[]={1,2,3,4,5,6,7,8,9,10};// 把 {3,4,5,6,7} 拷贝到 {1,2,3,4,5} 的位置my_memmove(arr,arr+2,20);// 正确输出:3 4 5 6 7 6 7 8 9 10for(inti=0;i<10;i++)printf("%d ",arr[i]);return0;}

三、memset:按字节设置内存值 🎨

memset的全称是memory set,作用是以字节为单位,将指定内存区域的每个字节都设置为目标值。它是初始化内存的常用工具,但极易因使用不当踩坑。

1. 函数核心要点(<string.h>)

  • 函数原型
    void*memset(void*ptr,intvalue,size_tnum);
    • ptr:要设置的内存起始地址
    • value:要设置的字节值(虽然参数是int,但实际只取低8位)
    • num:要设置的字节数
  • 核心特性
    ✅ 逐字节设置值,不是逐元素
    ✅ 常用于初始化字符数组,或把内存置为0

2. 正确用法:设置字符数组

#include<stdio.h>#include<string.h>intmain(){chararr[]="hello world";// 从第3个字符开始,连续设置5个字节为 'x'memset(arr+2,'x',5);printf("%s\n",arr);// 输出:hexxxxxldreturn0;}

3. 经典踩坑:设置非字符类型数组 ❌

很多人会用memset初始化整型数组,结果和预期不符:

#include<stdio.h>#include<string.h>intmain(){intarr[]={1,2,3,4,5,6,7,8,9,10};// 想把数组每个元素设为1 → 实际是每个字节设为1memset(arr,1,40);// 数组占 10*4=40 字节return0;}

内存监视结果:每个整型元素的4个字节都被设为0x01,因此每个元素的值是0x01010101(十进制 16843009),完全不是预期的1!

💡 结论:memset仅适合设置单字节数据(如char),或把内存置为0(value=0时,任何类型元素都是0)。

四、memcmp:按字节比较内存区域 🆚

memcmp的全称是memory compare,作用是以字节为单位,比较两块内存区域的前num个字节。它和strcmp的区别是:不依赖\0,可比较任意类型数据。

1. 函数核心要点(<string.h>)

  • 函数原型
    intmemcmp(constvoid*ptr1,constvoid*ptr2,size_tnum);
    • ptr1/ptr2:要比较的两块内存起始地址
    • num:要比较的字节数
  • 返回值规则
    ▶ 若ptr1 > ptr2→ 返回大于0的值
    ▶ 若ptr1 == ptr2→ 返回0
    ▶ 若ptr1 < ptr2→ 返回小于0的值

2. 实战示例:比较整型数组

#include<stdio.h>#include<string.h>intmain(){intarr1[]={1,2,3,4,5};// 内存布局:01 00 00 00 02 00 00 00 ...intarr2[]={1,2,3,4,0x12333301};// 第5个元素内存:01 33 33 12// 比较前17个字节(前4个元素+第5个元素的第1个字节)intr=memcmp(arr1,arr2,17);printf("%d\n",r);// 输出:正数(arr1第17字节是05,arr2是01)return0;}

写在最后 📝

内存操作函数是C语言直接操控内存的核心工具,和字符串函数相比,它们的通用性更强,适用范围更广,但也更容易因忽视字节操作的特性而踩坑。

掌握这些函数的关键在于三点:

  1. 区分memcpymemmove的使用场景(重叠/不重叠内存);
  2. 牢记memset逐字节设置,而非逐元素;
  3. 理解memcmp的比较规则是基于字节的ASCII码值。

这些函数的模拟实现逻辑,也是笔面试中考察指针和内存操作的高频考点,建议大家手动敲一遍代码,加深对底层原理的理解。至此,C语言字符串和内存操作的核心内容就全部讲解完毕啦!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询