1.memcpy
内存拷贝函数
1.1 介绍
函数介绍
void * memcpy ( void * destination, const void * source, size_t num ); 1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。 2.这个函数在遇到 '\0' 的时候并不会停下来。 3.如果source和destination有任何的重叠,复制的结果都是未定义的。 4.num的单位是字节,而不是元素个数 5.函数拷贝结束后会返回目标空间的起始地址
设计思想
因为具体的需要拷贝的数据类型不统一,所以返回值设为void*类型, 参数设计为前两个是void*类型的指针,最后一个是需要拷贝的字节个数
1.2 模拟实现
void* my_memcpy(void* dest,const void* src, size_t num) { void* ret = dest;//保存dest,便于返回 while (num--)//赋值num次 { *(char*)dest = *(char*)src;//强转为char*类型再解引用进行赋值操作 //(char*)dest++;强制类型转换是临时的强转 而运算符后置++的本质是创建临时变量再进行重新赋值 所以不能这样写 //int a = 10; //int b = a + 2; //类似于这种样子,a本身并未改变, //++(char*)dest;在某些编译器下可以,在某些编译器下则不行,不具备通用性 dest = (char*)dest + 1;//dest后移 src = (char*)src + 1;//src后移 } return ret; }
使用实例
int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; //01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00 09 00 00 00 10 00 00 00 //这些字节一个一个地拷贝 int arr2[20] = { 0 }; //将arr1中的内容,拷贝到arr2中 my_memcpy(arr2, arr1, 40);//40:总共拷贝40个字节,即10个整型 for (int i = 0; i < 20; i++) { printf("%d ", arr2[i]); // 1 2 3 4 5 6 7 8 9 10 0 0 0 0 0 0 0 0 0 0 } printf("\n"); return 0; } int main() { float arr1[] = { 1.0,2.0,3.0 }; float arr2[5] = { 0 }; //将arr1中的内容,拷贝到arr2中 my_memcpy(arr2, arr1, 8);//即只拷贝1.0和2.0这两个元素 for (int i = 0; i < 5; i++) { printf("%f ", arr2[i]); // 1.000000 2.000000 0.000000 0.000000 0.000000 } printf("\n"); return 0; }
1.3 注意事项
如果source和destination有任何的重叠,复制的结果都是未定义的。
即memcpy函数是用来处理不重叠的内存拷贝的
大家来看一下这个代码
int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; //我们以为的 1 2 1 2 3 4 5 8 9 10 //真实的: 1 2 1 2 1 2 1 8 9 10 //所以memcpy函数是用来处理不重叠的内存拷贝的 my_memcpy(arr1+2, arr1, 20); for (int i = 0; i < 10; i++) { printf("%d ", arr1[i]); } printf("\n"); return 0; }
下面给大家画图解释一下这个现象的产生
可见此时从前向后赋值的话会导致3和4被1和2覆盖,继而导致一连串的覆盖结果,那么如何解决这种问题呢?
于是memmove函数登场了
2.memmove
内存"移动"函数
其实就是内存拷贝函数的升级版
2.1 模拟实现
void* my_memmove(void* dest, const void* src, size_t num) { assert(dest != NULL); assert(src != NULL); void* ret = dest; 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; }
应用实例
int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; my_memmove(arr1 + 2, arr1, 20); // 1 2 1 2 3 4 5 8 9 10 //my_memmove(arr1, arr1 + 2, 20); // 3 4 5 6 7 6 7 8 9 10 for (int i = 0; i < 10; i++) { printf("%d ", arr1[i]); } }
至于为何要分为从前向后拷贝与从后向前拷贝,下面给大家画图看一下
注意: 在VS编译器下,memcpy可以实现重叠拷贝 但是并不是所有的编译器下,memcpy都能实现重叠拷贝 但是C语言标准只要求memcpy实现不重叠拷贝就可以 也就是C标准只要求memcpy考60分即可 但是VS下,memcpy考了100分,超额完成任务 memmove当然也能拷贝不重叠的拷贝,要求100分,实际100分
3.memcmp
内存比较函数
int memcmp(const void* ptr1, const void* ptr2, size_t num); 比较从ptr1和ptr2指针开始的num个字节 返回值取值跟strcmp相同
4.memset
内存设置函数
void * memset ( void * ptr, int value, size_t num ); 把ptr指向的数据往后数num个字节,赋值为value
避坑指南
int main20() { //char arr[] = "hello bit"; //memset(arr + 1, 'x', 4);//以字节为单位的设置,一次性把多个字节全部设置为'x' //printf("%s\n", arr); //注意不要踩坑 int arr[10] = { 0 }; memset(arr, 1, 10); //memset无法将arr数组的所有元素都设置为1,只能将所有的字节全部设置为1 //调试看内存 for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } printf("\n"); //你以为的:1 1 1 1 1 1 1 1 1 1 //真实: 16843009 16843009 257 0 0 0 0 0 0 0 return 0; }
在这里我们可以看出memset函数并未成功将arr数组中的10元素均设置为1,而是将10个字节设置为了1,即memset函数无法针对数据类型的整体进行赋值,而只能针对单个字节进行赋值.
以上就是C语言内存函数的讲解,希望能对大家,有所帮助,谢谢.