C 语言常见内存操作函数: memcpy、memmove、memset、memcmp
2026/8/30 16:38:38 网站建设 项目流程

文章目录

    • 1.memcpy:复制不重叠的内存区域
      • 1.1 基本用法
      • 1.2 重叠区域不使用 memcpy
      • 1.3 模拟实现
    • 2. memmove:允许重叠的内存移动
      • 2.1 基本用法
      • 2.2 拷贝覆盖数组中的一段元素
      • 2.3 对于重叠部分内存的拷贝处理
      • 2.4 模拟实现
    • 3. memset:用一个字节值填充 n 个字节
      • 3.1 基本用法
      • 3.2 memset 的 value 是字节,不是 int 数组元素
      • 3.3 模拟实现
    • 4. memcmp:比较前 n 个字节
      • 4.1 基本用法
      • 4.2 与 strcmp 的区别
      • 4.3 模拟实现
    • 5. 函数选择速查表
    • 6. 常见错误与修正习惯
      • 6.1 把元素数量当成字节数量
      • 6.2 目标空间不足
      • 6.3 误用返回值
    • 7. 总结

1.memcpy:复制不重叠的内存区域


1.1 基本用法

void*memcpy(void*destination,constvoid*source,size_tnum);

参数说明:

  • destination:指向目标数组的指针,待复制内容将存放于此,该指针被强制转换为void*类型。
  • source:指向数据源的指针,待复制的数据来源,该指针被强制转换为const void*类型。
  • num:需要复制的字节数量。
#include<stdio.h>#include<string.h>intmain(){intsrc[]={10,20,30,40,50};intdst[5]={0};memcpy(dst,src,sizeof(src));for(inti=0;i<5;i++){printf("%d ",dst[i]);}return0;}

运行结果:

sizeof(src)得到整个数组的字节数,所以这里会复制 5 个 int。若只想复制前两个元素,应写 2 * sizeof src[0],不能只写 2;2 表示两个字节,而不是两个整数。

memcpy 返回 dst 的首地址,因此可以把返回值传给其他函数,但通常直接使用目标数组更容易读懂。

1.2 重叠区域不使用 memcpy

chartext[8]="abcdef";memcpy(text+1,text,5);// 源和目标重叠,行为未定义

memcpy 的前提是源区域和目标区域完全不重叠。不确定是否重叠时,使用 memmove。

1.3 模拟实现

void*my_memcpy(void*des,constvoid*src,size_tcount){void*res=des;if(des==NULL||src==NULL){return;}while(count--){*(char*)des=*(char*)src;des=(char*)des+1;src=(char*)src+1;}return(res);}

效果验证:

intmain(){intarr1[]={1,2,3,4,5,6,7,8,9,10};intarr2[10]={0};my_memcpy(arr2,arr1,20);//将arr1中20字节(5个int元素)内容拷贝到arr2for(inti=0;i<10;i++){printf("%d ",arr2[i]);}return0;}

运行结果:

这个模拟版本仍要求区域不重叠、地址有效且目标容量至少为 n 字节;

2. memmove:允许重叠的内存移动


2.1 基本用法

void*memmove(void*destination,constvoid*source,size_tnum);

参数说明:

  • destination:指向目标数组的指针,待复制内容将存放于此,指针会被强制转换为void*类型。

  • source:指向数据源的指针,待复制的数据来源,指针会被强制转换为const void*类型。

  • num:需要复制的字节数量。

#include<stdio.h>#include<string.h>intmain(){chartext[16]="abcdef";memmove(text+1,text,strlen(text)+1);printf("%s\n",text);return0;}

运行结果:

2.2 拷贝覆盖数组中的一段元素

intvalues[]={10,20,30,40,50};size_tcount=5;size_tremove_at=1;size_tremove_count=2;memmove(&values[remove_at],&values[remove_at+remove_count],(count-remove_at-remove_count)*sizeofvalues[0]);count-=remove_count;

移动后数组前 3 个有效元素是10, 40, 50。尾部的两个位置仍然保留旧值,但它们已经不属于有效范围;如果数据信息不用了的话,可以再用 memset 清理。memmove 只负责搬运,不会自动更新 count。

2.3 对于重叠部分内存的拷贝处理

memmove内部会判断源地址和目标地址的位置,自动选择正向拷贝或者反向拷贝,所以可以安全处理内存重叠。对于内存重叠,举例如下:

数组:{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } src = &buf[2], dest = &buf[0], n = 4 也就是要把 3,4,5,6,7 复制到 1,2,3,4,5 或者反过来: src = &buf[0], dest = &buf[2], n =4 把 1,2,3,4,5 复制到 3,4,5,6,7

当使用memcpy 的实现思路时,会对于源地址以及目标地址进行从头到尾依次拷贝覆盖。
问题:如果destsrc的前面,并且区域重叠
比如:src=buf[0], dest=buf[2], n=4

  • 原始数据:0,1,2,3,4,5
  • 第一步:d[0]=s[0]→ buf [2]=1
  • 第二步:d[1]=s[1]→ buf [3]=2
  • 第三步:d[2]=s[2]s [2] 已经被前面覆盖成 1 了!
    读到的不是原来的 3,而是已经被改写的值 → 结果出错,如下所示。


而memmove的实现会先判断地址大小:

  • 目标在源前面:正向拷贝(和memcpy一样)
  • 目标在源后面,且重叠:反向拷贝!从最后一个字节往前复制

src=buf[0], dest=buf[2], n=4为例,我们先复制最后一个字节,再往前:
先拷贝 s [3] → d [3],再 s [2]→d [2]……
不会提前覆盖还没读取的源数据,完美解决重叠问题。

2.4 模拟实现

void*my_memmove(void*des,constvoid*src,size_tcount){void*res=des;if(des==NULL||src==NULL){return;}//des起点在src前//正常从前往后复制if(des<=src){while(count--){*(char*)des=*(char*)src;des=(char*)des+1;src=(char*)src+1;}}//从后往前复制else{des=(char*)des+count-1;src=(char*)src+count-1;while(count--){*(char*)des=*(char*)src;des=(char*)des-1;src=(char*)src-1;}}return(res);}

效果验证:

intmain(){intarr1[]={1,2,3,4,5,6,7,8,9,10};//将arr1首元素后20字节(5个int元素)拷贝到 arr1 + 2地址处my_memmove(arr1+2,arr1,20);for(inti=0;i<10;i++){printf("%d ",arr1[i]);}return0;}

运行结果:

3. memset:用一个字节值填充 n 个字节


3.1 基本用法

void*memset(void*ptr,intvalue,size_tnum);

参数说明:

  • ptr:指向待填充内存块的指针。

  • value:要设置的值。该值以int类型传入,但函数会将该值转换为unsigned char类型后填充内存块。

  • num:需要设置为该值的字节数量。

charbuffer[32];memset(buffer,0,sizeofbuffer);

memset用于清零最为常见,这会把 32 个字节都写成 0,常用于初始化字符缓冲区或清空一块原始存储。

3.2 memset 的 value 是字节,不是 int 数组元素

inta[4];memset(a,1,sizeof(a));

这里每个字节都被写成 0x01。在常见机器上,每个 int 可能变成 0x01010101(十进制16843009),而不是 1。

value的类型虽然是int,但函数只取它转换后的unsigned char低 8 位来重复填充。例如memset(buf, 0x123, n)实际填入的通常是0x23;代码应避免依赖超出unsigned char范围的值。

3.3 模拟实现

void*my_memset(void*ptr,intvalue,size_tn){unsignedchar*p=(unsignedchar*)ptr;unsignedcharbyte=(unsignedchar)value;size_ti;for(i=0;i<n;i++){p[i]=byte;}returnptr;}

效果验证:

intmain(){inta[5];//将a数组内5个元素全设置为0my_memset(a,0,5*sizeof(a[0]));for(inti=0;i<5;i++){printf("%d ",a[i]);}return0;}

运行结果:

4. memcmp:比较前 n 个字节


4.1 基本用法

intmemcmp(constvoid*ptr1,constvoid*ptr2,size_tnum);

参数说明:

  • ptr1:指向一块内存的指针。
  • ptr2:指向另一块内存的指针。
  • num:需要比较的字节数量。
返回值含义
<0两块内存中第一个不匹配的字节,按unsigned char解读后,ptr1该字节的值小于ptr2对应字节
0两块内存指定长度内的内容完全相等
>0两块内存中第一个不匹配的字节,按unsigned char解读后,ptr1该字节的值大于ptr2对应字节
unsignedchara[]={1,2,3};unsignedcharb[]={1,2,4};if(memcmp(a,b,sizeof(a))==0){printf("完全相同\n");}else{printf("存在差异\n");}

memcmp 按 unsigned char 逐字节比较,遇到第一处不同就停止;返回值小于 0、等于 0 或大于 0,分别表示第一处不同字节的大小关系。标准不保证非零返回值一定是 -1 或 1,不要写 memcmp(…) == 1 来判断不相等。

4.2 与 strcmp 的区别

charraw[]={'a','\0','b'};charraw2[]={'a','\0','c'};printf("%d\n",memcmp(raw,raw2,sizeof(raw)!=0);// 1 不相同printf("%d\n",strcmp(raw,raw2)==0);// 1 相同

strcmp 在第一个\0处就结束,所以它把两段数据都看成amemcmp则按指定的 3 个字节比较,能发现最后一个字节不同。处理二进制数据或固定长度字段时,memcmp才是合适方向。

4.3 模拟实现

intmy_memcmp(constvoid*lhs,constvoid*rhs,size_tn){constunsignedchar*a=(constunsignedchar*)lhs;constunsignedchar*b=(constunsignedchar*)rhs;size_ti;for(i=0;i<n;i++){if(a[i]!=b[i]){return(a[i]>b[i])?1:-1;}}return0;}

效果验证:

charstr1[]="abcde";charstr2[]="abcdef";if(!my_memcmp(str1,str2,sizeof(str2))){printf("相同\n");}else{printf("存在差异\n");}

运行结果:

5. 函数选择速查表


需求函数是否寻找 ‘\0’是否允许源/目标重叠
复制 n 个字节memcpy
移动 n 个字节memmove
重复填充 n 个字节memset不适用
比较 n 个字节memcmp不修改输入
复制字符串strcpy

6. 常见错误与修正习惯


6.1 把元素数量当成字节数量

对于int values[10],复制 10 个元素应写10 * sizeof values[0],或在源数组仍是数组时写sizeof(values)

6.2 目标空间不足

memset(buf, 0, n)是逐字节清零;memset(numbers, 1, sizeof(numbers))不是把每个整数设为 1(而是逐个字节设置为1)。

6.3 误用返回值

复制类函数返回目标地址,不是复制了多少字节;memcmp 返回的是符号结果,不是布尔值;注意使用前先看函数原型,再写判断。

7. 总结


memcpy适合不重叠复制,memmove专门处理可能重叠的移动,memset重复写入一个字节,memcmp比较固定长度的字节序列,它们共同遵守一条规则:函数只相信你传入的地址和 n,不会替你猜测容量、类型或数据含义。

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

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

立即咨询