C语言内存函数超详解
2025-06-24 12:29:54
来源:新华网
文章目录
- 前言
- 1. memcpy
- 1. 1 memcpy 的使用
- 1. 2 memcpy 的模拟实现
- 2. memmove
- 2. 1 memmove 使用
- 3. memset
- 3. 1 memset 函数的使用
- 3. 2 memset 的模拟实现
- 4. memcmp
- 4. 1 memcmp 函数的使用
- 4. 2 memcmp 的模拟实现
前言
C语言为我们提供了字符串的一些函数,比如复制,比较等等,但是这些函数只能用在字符串上,而C语言的数据类型显然不止字符串一种,那应该怎么办?难道要把每一种数据类型都包装几个函数供我们使用吗?显然这太过臃肿。
为了解决这个问题,C语言在string.h
库中提供了内存函数供我们使用。
1. memcpy
void*memcpy(void*destination,constvoid*source,size_tnum );
- 函数
memcpy
从source
的位置开始向后复制num
个字节的数据到destination
指向的内存位置。 - 这个函数在遇到
'\0'
的时候并不会停下来。 - 如果
source
和destination
有任何的重叠,复制的结果都是未定义的。也就是说,source
和destination
后面的num
个字节之间不能有重叠的部分!
1. 1 memcpy 的使用
#include #include intmain(){ intarr1[10]={ 0,1,2,3,4,5,6,7,8,9};intarr2[10]={ 0};memcpy(arr2,arr1,sizeof(arr1));//sizeof(arr)是40个字节,也就是10个 int 类型的变量for(inti =0;i <10;i++)printf("%d ",arr2[i]);return0;}
memcpy函数操作的对象是内存中的数据,因此,无论是什么类型的数据,只要大小给的正确,都可以进行复制。
1. 2 memcpy 的模拟实现
在开始前,我们再来看一眼 memcpy 的声明:
void*memcpy(void*destination,constvoid*source,size_tnum );
为什么这里要用 void*
指针?显然是为了让这个函数能接受任何类型的指针,但在函数体内部,这样的指针是不能解引用的,那该怎么办?
我们知道,memcpy
函数是以字节为单位复制数据的,而 char
类型的数据的大小就是一个字节,那么我们只需要在函数体里将两个形参都强制类型转换为char*
类型,就可以完成操作了。
void*my_memcpy(void*des,constvoid*source,size_tnum){ //尽管说 memcpy 不能处理重叠的数据,但在模拟实现也不需要管数据是否重叠,因为这是未定义的char*cur1 =(char*)des;//当然,你也可以省略这两步,在下面的循环中将两个形参进行临时转换char*cur2 =(char*)source;while(num--)*cur1++=*cur2++;returndes;//注意 memcpy 的返回类型是void* ,不是 void ,它返回的是目标内存的起始位置}
2. memmove
void*memmove(void*destination,constvoid*source,size_tnum );
- 和
memcpy
的差别就是memmove
函数处理的源内存块和目标内存块是可以重叠的。 - 如果源空间和目标空间出现重叠,就得使用
memmove
函数处理。
2. 1 memmove 使用
既然 memmove
的特点就是能够处理重叠的数据,那我们就让它处理重叠的数据看看效果。
#include #include intmain(){ intarr[10]={ 0,1,2,3,4,5,6,7,8,9};memmove(arr+1,arr,9*sizeof(int));for(inti =0;i <10;i++){ printf("%d ",arr[i]);}return0;}
输出结果:
我们来简单的分析一下
memmove(arr+1,arr,9*sizeof(int));
这段代码是怎么工作的。arr+1
是目标位置, arr
是源位置,arr
向后复制 9 个 int
类型的大小到arr+1
的后面,如果我们按照我们上面模拟实现的 memcpy
的思路去分析,可以发现,arr+1
位置的数据,在第一次复制之后,和arr的数据一样了,那么第二次复制时,复制给 arr+2
的数据就也是 arr 的数据了,显然这样不行。
那该怎么办?答案很简单,就是从后往前复制,这样就不会出现上面的问题了。
这样就大功告成了吗?
按照上面的思路,我们来分析一下这个代码:
memmove(arr,arr +1,9*sizeof(int));
如果按照上面的思路从后往前复制,会是什么样的?
第一次复制是将 arr+9
位置的数据复制到 arr+8
,第二次复制是将 arr+8
的数据复制到 arr+7
,你看,之前的问题又出现了,arr+7
位置的数据和 arr+9
的数据一样了。
所以应该怎么办?
很简单,分情况:我们发现,当目标空间的起始位置在源空间的起始位置的前面时,我们需要从前往后复制,而目标空间的起始位置在源空间的起始位置的后面时,我们需要从后往前复制,这样就大功告成了。
函数接口:
void*my_memmove(void*dest,constvoid*source,size_tnum){ char*curdest =(char*)dest;char*cursou =(char*)source;if(dest >source){ while(num--)*(curdest +num)=*(cursou +num);}else{ while(num--)*(curdest++)=*(cursou++);}returndest;}
我们再写 main
函数,对两种结果分别进行测试
intmain(){ intarr[10]={ 0,1,2,3,4,5,6,7,8,9};//my_memmove(arr+1, arr , 9 * sizeof(int));//结果1my_memmove(arr,arr +1,9*sizeof(int));//结果2for(inti =0;i <10;i++){ printf("%d ",arr[i]);}return0;}
结果1:
结果2:
可以看到,无论是哪一种情况,my_memmove
函数都实现了预期的效果。
3. memset
void*memset(void*ptr,intvalue,size_tnum );
3. 1 memset 函数的使用
memset
是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。
使用示例:
#include #include intmain(){ charstr[]="Hello World.";memset(str,'x',strlen(str)-2);//strlen是求字符串长度的函数printf("%s\n",str);return0;}
输出结果:
看到这里你可能会有疑惑:为什么value
是一个int
类型的变量却可以接收 char 类型的形参?实际上value
在这里接收的是'x'
的ASCII码值。
3. 2 memset 的模拟实现
和 memcpy
的思路很像,只是没有源位置了,换成了一个 value
。
#include #include void*my_memset(void*ptr,intvalue,size_tnum){ char*cur =(char*)ptr;while(num--)*(cur++)=value;returnptr;}intmain(){ charstr[]="Hello World.";my_memset(str,'x',strlen(str)-2);printf("%s\n",str);return0;}
输出结果同上。
4. memcmp
intmemcmp(constvoid*ptr1,constvoid*ptr2,size_tnum );
- 比较从ptr1和ptr2指针指向的位置开始,向后的num个字节
- 返回值如下:
memcmp
是将一个字节的内容看做unsigned char
类型进行比较的,其他的和strcmp
一致。
图片来源: cplusplus
4. 1 memcmp 函数的使用
使用示例:
#include #include intmain(){ charbuffer1[]="DWgaOtP12df0";charbuffer2[]="DWGAOTP12DF0";intn;n =memcmp(buffer1,buffer2,sizeof(buffer1));if(n >0)printf("'%s' is greater than '%s'.\n",buffer1,buffer2);elseif(n <0)printf("'%s' is less than '%s'.\n",buffer1,buffer2);elseprintf("'%s' is the same as '%s'.\n",buffer1,buffer2);return0;}
输出结果:
4. 2 memcmp 的模拟实现
思路基本和 strcmp
一致,只是多了一步强制类型转换,这里不再赘述。
#include #include intmy_memcmp(constvoid*ptr1,constvoid*ptr2,size_tnum){ unsignedchar*cur1 =(unsignedchar*)ptr1;unsignedchar*cur2 =(unsignedchar*)ptr2;while(num--){ if(*cur1 !=*cur2)return*cur1 -*cur2;cur1++,cur2++;}return0;}intmain(){ charbuffer1[]="DWgaOtP12df0";charbuffer2[]="DWGAOTP12DF0";intn;n =my_memcmp(buffer1,buffer2,sizeof(buffer1));if(n >0)printf("'%s' is greater than '%s'.\n",buffer1,buffer2);elseif(n <0)printf("'%s' is less than '%s'.\n",buffer1,buffer2);elseprintf("'%s' is the same as '%s'.\n",buffer1,buffer2);return0;}
如果喜欢这篇博客的话不妨顺手点个赞,收藏,评论,关注!
我会持续更新更多优质文章!!