内存函数
以下函数的头文件:string.h
- 针对内存块进行处理的函数
memcpy
函数原型:
void*memcpy(void* destination,constvoid* source,size_t num);
目标空间地址 源空间地址
- num,被拷贝的字节个数
- 返回目标空间的起始地址
- 从source函数向后拷贝num字节个数据到目标空间
- 目标空间地址和源空间地址有重叠部分,有重叠内存的拷贝,结果是未定义的
- C语言标准规定:memcpy可以不负责有重叠内存的拷贝- 而vs编译器的库函数也可以实现重叠内存的拷贝。
#include<stdio.h>#include<string.h>intmain(){int arr1[20]={1,2,3,4,5,6};int arr2[20]={0};memcpy(arr2, arr1,sizeof(int)*5);for(int i =0; i <5; i++){printf("%d ", arr2[i]);}return0;}
通过函数原型也发现,这个memcpy函数是一个函数参数,函数返回类型都是void无符号类型,这是说明该函数是一个,泛型函数,它可以接收任意类型的参数,使同一个函数能用于多种类型的数据。
该用例说明了,将数组arr1从第一个元素开始的20个字节拷贝到arr2中。
模拟memcpy函数
void*my_memcpy(void* dest,constvoid* src,size_t num){void* ret = dest;assert(dest && src);while(num--){*(char*)dest =*(char*)src;
dest =(char*)dest +1;
src =(char*)src +1;}return ret;}
该函数是泛型函数,在返回类型和参数上要保持一致,后面的size_t num参数用来接收拷贝的字节个数。
能够进行任意数据类型的拷贝,这里只能将源空间一个一个字节拷贝到目标空间里,而void类型不是4个字节,所以将,dest和src强制类型转换为char类型,这样每循环一次就拷贝一个字节的内容,然后让指针dest和src向后走一个字节。
(char*)dest++;
与
dest = *(char*)dest + 1;
的区别:
c/c++里的强制类型转换,是临时的,
(char*)dest++;
试图将一个 void *类型的dest强制类型转换,然后++,语法是有问题的,对 (char *)dest使用++,实际上是
(char* dest) = dest + 1;
,将无符号类型的指针赋给 char *类型的指针编译器会报错的
最后将目标空间的起始地址返回即可。
使用assert断言是因为,目标空间和源空间都不能为空,否则就是对空指针解引用,加一条assert是代码具有更好的健壮性。
memmove
函数原型:
void*memmove(void* destination,constvoid* source,size_t num);
- 负责重叠内存的拷贝
- 用法与memcpy相同。
- memmove可以是memcpy的替代,它支持更多的使用场景,重叠内存的拷贝。
模拟memmove函数
俩个函数的功能及其相似,它们在实现上的不同,模拟memcpy函数是,对字节进行拷贝时,是将source里的字节从前向后一个一个拷贝的,如图:
source里一共有12个字节,模拟的memcpy函数将从数字2开始的第一个字节,一个一个拷贝到目标空间里。
若两快空间重叠时会发生哪些情况:
- dest指针大于source指针,目标空间与源空间重叠,但目标空间起始地址小于源空间,此时针对情况一,如上图橘色与蓝色重叠部分
- dest指针小于source指针,目标空间与源空间重叠,但目标空间起始地址大于源空间,此时针对情况二,如上图绿色与蓝色重叠部分
情况一:
- 当使用从前向后一个一个字节的拷贝时, 0 被拷贝为 2,1 被拷贝为 3,2 被拷贝为 4,结果与预期相符和,所以当目标空间与源空间重叠,但目标空间起始地址小于源空间时,采用从前向后一个字节一个字节的拷贝。
情况二:
- 对应情况二使用从前向后一次拷贝字节时,将源空间内容拷贝到密目标空间,此时 4 被拷贝为 2 , 5 被拷贝为 3 , 6被拷贝为2。
- 与预期结果并不相符和,预期拷贝后的结果时 0 1 2 3 2 3 4。
- 实际结果为,0 1 2 3 2 3 2。
可以发现,使用从前向后一起拷贝字节时,源空间的内容覆盖掉,它还没有拷贝就已经被覆盖了,并将其放置在目标空间的最后一个位置。解决这种办法也很好理解,既然从前向后拷贝不行,那我们从后向前拷贝不久可以了。这样就避免了,还没有将源空间的数据拷贝到目标空间就被覆盖的情况。
将source指针和destination指针向后偏移num个字节,
(char*)dest + num (char*)src + num
,这样它两就指向最后一个字节,从后向前拷贝,每拷贝完一个字节后 num–。
void*my_memmove(void* dest,constvoid* src,size_t num){void* ret = dest;assert(dest && src);if(dest > src)//后向前{while(num--){*((char*)dest + num)=*((char*)src + num);}}else{while(num--){*(char*)dest =*(char*)src;
dest =*(char*)dest +1;
src =*(char*)src +1;}}return ret;}
当dest > src时,从后向前,第一步将dest和src偏移到最后一个位置,
(char*)dest + num (char*)src + num
,然后进行赋值 ,
*((char *)dest ) = *((char *)src );
,其代码内讲多块内容合并在一起操作,通过控制num大小,来控制了dest和src偏移的位置,而循环结束的条件的num为0,这样就有了一条很精简的代码。
memset
函数原型:
void*memset(void* ptr,int value,size_t num);
被填充空间 改变的内容 设置的字节个数
memset,是用来设置内存的,将内存以字节为单位,
- 将每个字节设置为 value的内容
很朴素的用法,没有过多的变化,但结果往往会让人出乎意料~。
intmain(){int arr1[20]={1,2,3,4,5,6};int arr2[20]={0};memset(arr2,9,5);memset(arr1,8,5);for(int i =0; i <6; i++){printf("%d ", arr1[i]);}printf("\n");for(int i =0; i <5; i++){printf("%d ", arr2[i]);}return0;}
这不是一个使用的案例,通过运行代码能够发现数组arr1和arr2的结果相当的大,这是因为memset是在字节上设置内容,这里的第一条memset语句,将数组arr2里的五个字节内容,都放置了一个数字9,而内存里的 09 09 09 09实际上是16进制数,0x09090909,所以在打印的结果上会很大。
数字arr1的结果,同数组arr2一样,即使在传递的数组这块内容已经放的有值,memset还会将其覆盖。
这里以字符串为例,字符串的大小是一个字节,这里我试图将字符数组arr1里的前三个字节内容置为x。
intmain(){char arr1[10]="abcdef";memset(arr1,'x',3);return0;}
观察结果,memset函数指哪打哪~,也没有在最后一个x后多余的放置
'\0'
。
memcmp
函数原型:
intmemcmp(constvoid* ptr1,constvoid* ptr2,size_t num);
比较字节大小:
- ptr1 > ptr2 返回大于0
- ptr1 < ptr2 返回小于0
- 等于返回 0
- num是指比较字节个数
memcmp函数与字符串函数里的strncmp功能很类似,前者是通过比较字节大小,后者是比较字符大小。
intmain(){int arr1[]={1,2,5};int arr2[]={1,2,6};printf("%d \n",memcmp(arr1, arr2,8));return0;}
arr1前12个字节:
01 00 00 00 02 00 00 00 05 00 00 00
arr2前12个字节
01 00 00 00 02 00 00 00 06 00 00 00
试图比较前8个字节的大小时,返回的结果是0,每个字节上的内容都是相等的,而
memcmp(arr1, arr2, 9);
,比较前9个字节大小时,将返回小于0的数字, 06 > 05 。
cmp(arr1, arr2, 8));
return 0;
}
arr1前12个字节:
01 00 00 00 02 00 00 00 05 00 00 00
arr2前12个字节
01 00 00 00 02 00 00 00 06 00 00 00
试图比较前8个字节的大小时,返回的结果是0,每个字节上的内容都是相等的,而
memcmp(arr1, arr2, 9);
,比较前9个字节大小时,将返回小于0的数字, 06 > 05 。
版权归原作者 闲晨 所有, 如有侵权,请联系我们删除。