memcpy函数
函数原型:void * memcpy ( void * destination, const void * source, size_t num );
头文件: <string.h>
作用:从source的起始位置开始向后复制num个字节的数据到destination指向的内存空间
注意事项:
1、返回的是目标空间的起始地址
2、源内存空间大小 <= 目标内存空间大小
3、memcpy函数只能处理源内存空间与目标内存空间不重叠时的数据拷贝
(arr+2,arr,20)是想将12345拷贝至34567的内存空间,希望的输出结果是12123458910,但是实际结果为12121218910,这就是因为使用memcpy函数时出现了以下的情况:
当我们完成1和2与3和4的拷贝时,3和4的内存空间其实已经变成1和2了,所以当我们想把原来3和4位置上的数拷贝给5和6时其实是将1和2重新拷贝给了5和6,此时5和6的内存空间也变成了1和2,7和8的情况也是一样的,只不过num=20所以8侥幸逃过一劫。
memcpy函数模拟实现
#define _CRT_SECURE_NO_WARNINGS 1 #include<ctype.h> #include<stdio.h> #include<string.h> #include<assert.h> #include<errno.h> my_memcpy(void* dest, const void* 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; } int main() { int arr1[10] = { 0 }; int arr2[] = { 1,2,3,4,5,6,7,8 }; my_memcpy(arr1, arr2, 20); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }
memmove函数
函数原型:void * memmove ( void * destination, const void * source, size_t num );
头文件: <string.h>
作用:从source的起始位置开始向后复制num个字节的数据到destination指向的内存空间
注意事项:
1、返回目标空间的起始地址
2、源内存空间大小 <= 目标内存空间大小
3、当两内存空间存在重叠时,拷贝顺序分为从前向后、从后向前拷贝和随意拷贝三种情况
从后向前拷贝:
具体流程:5->7、 4->6、 3->5、 2->4、 1->3
从前向后拷贝:
具体流程: 3->1、 4->2、 5->3、 6->4、 7->5
随意拷贝:
具体流程:7->8、6->9、5->10 或 3->8、4->9、5->10(大致意思如下,后面没画完)
针对上面的三种情况,我们可以得到memmove函数模拟实现要用到的判断条件:
①if dest < src //从前向后
②if dest >= src && dest <= src + num; //从后向前
③if dest >src + num || dest + num < src; //任意方向均可
memmove函数模拟实现
#include <stdio.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <stdio.h> #include <string.h> void* my_memmove(void* dest, const void* src, size_t n) { unsigned char* d = (unsigned char*)dest; const unsigned char* s = (const unsigned char*)src; if (d == s) { return d; } if (s + n < d || d + n < s) { // 没有内存重叠 while (n--) { *(d++) = *(s++); } } else { // 存在内存重叠 if (d < s) { // 目标地址在源地址之前,从前向后拷贝 while (n--) { *(d++) = *(s++); } } else { // 目标地址在源地址之后,从后向前拷贝 d += n - 1; s += n - 1; while (n--) { *(d--) = *(s--); } } } return dest; } int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; //my_memmove(arr+2, arr, 20); my_memmove(arr, arr + 2, 20); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; }
此时,当我们此时将模拟函数切换成memcpy函数我们会发现:
明显的,这次居然成功了!!!这就很奇怪了🙄,这是因为:
某些版本的VS标准库中的memcpy函数也能实现重叠内存拷贝,但还是不建议使用
关于以上两个函数的总结:
二者都是用于在内存之间进行数据拷贝的函数,但它们在处理重叠内存区域时有所不同:
对于memcpy函数:
- 不会检查源地址和目标地址是否重叠
- 当源和目标内存区域重叠时,可能会导致未定义行为,结果不可预测
对于memmove函数:
- 能正确处理源和目标内存区域重叠情况。即使两个内存区域有部分或完全重叠
- 在处理重叠情况时,使用了一种更加安全且效率较低的算法来执行拷贝操作
结论:
- 若确定两块内存空间不重叠,则可以使用 memcpy函数,它的执行速度更快
- 若无法确定两块内存空间是否重叠,或者确实存在重叠情况,应使用memmove函数来保证拷贝操作的正确性
在使用这些函数时,请确保目标内存区域足够大以容纳源数据,并进行适当的错误处理
memset函数
函数原型:void * memset ( void * ptr, int value, size_t num );
头文件: <string.h>
作用:将内存中的值以字节为单位设置成想要的内容
注意事项:
1、返回目标空间的起始地址。
2、value表示想要更改的内容,ptr表示要更改的目标空间,num表示要操作的个数
3、memset函数是以字节为单位设置内存值。
#define _CRT_SECURE_NO_WARNINGS 1 #include<ctype.h> #include<stdio.h> #include<string.h> #include<assert.h> #include<errno.h> int main() { char ch[] = "hello world"; memset(ch, 'x', 5); printf("%s\n", ch); return 0; }
memset函数模拟实现
这次没有模拟实现🤡,这次要利用监视和内存窗口来更好的理解,它在内存中的操作
#define _CRT_SECURE_NO_WARNINGS 1 #include<ctype.h> #include<stdio.h> #include<string.h> #include<assert.h> #include<errno.h> int main() { int arr[] = {1,2,3,4,5,6,7,8,9,10}; memset(arr,0,20); return 0; }
运行开始前我们点击”调试“-”窗口“-”内存“,随机选取一个内存窗口,同时打开监视窗口,当程序运行至return 0时我们会发现内存中会发生改变。
每次分配的地址都会发生改变,所以每次查询首元素地址时直接输入数组名然后回车即可
内存中的变化:
监视中的变化:
~通过以上变化我们就可以更充分的认识“memset函数是以字节为单位设置内存值”这句话的意思~
memcmp函数
函数原型:int memcmp ( const void * ptr1, const void * ptr2, size_t num );
头文件: <string.h>
作用:从ptr1和ptr2指向的位置开始,向后比较num个字节(str1和str2分别指向两个内存块)
注意事项:
1、ptr1小于ptr2时,返回小于零的整数
2、ptr1等于prt2时,返回零
3、ptr1大于ptr2时,返回大于零的整数
#define _CRT_SECURE_NO_WARNINGS 1 #include<ctype.h> #include<stdio.h> #include<string.h> #include<assert.h> #include<errno.h> int main() { char arr1[] = {1,2,3,4,5}; char arr2[] = { 1,3,5,7,9 }; int ret = memcmp(arr1,arr2,5); printf("%d ", ret); return 0; }
当我们展开八列的时候我们会发现,数组1的前五个字节为01 00 00 00 02,而数组2的前五个字节为01 00 00 00 03,所以数组一的内存块儿是小于数组二的内存块,返回结果为: