2017酷站推荐网站,白城网站建设哪家好,东莞学网站建设难吗,服务器迁移到另一台服务器在上一篇博客中我为大家分享了一些常用的字符串函数#xff0c;以及它们的用法和模拟实现。通过字符串函数中的strcpy#xff0c;我们能够做到将一个字符串中的内容拷贝到另一个字符串上#xff0c;可如果有一天我们想把一个整型数组中的内容拷贝到另一个整型数组中呢#…在上一篇博客中我为大家分享了一些常用的字符串函数以及它们的用法和模拟实现。通过字符串函数中的strcpy我们能够做到将一个字符串中的内容拷贝到另一个字符串上可如果有一天我们想把一个整型数组中的内容拷贝到另一个整型数组中呢这样看来好像strcpy就不适用了因为它只能对字符串进行拷贝。那么今天所提到的内存函数就能够实现整型数组甚至其他类型的拷贝让我们开始今天的学习吧~
一、memcpy函数
我们可以看到memcpy函数的返回类型以及参数类型大多都是void类型这也就使得它能够接收不同类型的数据并且能够对不同类型的数据加以处理~
①memcpy函数的使用
memcpy函数的作用是将num字节的值从源指向的位置直接复制到目标指向的内存块。由于参数为void*类型所以源指针和目标指针指向的对象的底层类型与此函数无关。(注:这个函数在遇到 \0 的时候并不会停下来。)
memcpy函数的第一个参数void* destination指向目标起始位置代表想要改变的初始位置第二个参数const void* source指向想要复制的数据的起始位置它的作用是作为模板为目标复制字节第三个参数size_t表示要复制的字节数。那么了解了memcpy函数的组成让我们举个例子尝试一下使用memcpy函数拷贝整型数组吧~
int main()
{int arr0[20] { 0 };int arr1[] { 1,2,3,4,5,6,7,8,9,10 };memcpy(arr0, arr1, 10 * sizeof(int));for (int i 0; i 10; i){printf(%d , arr0[i]);}return 0;
}甚至我们还能做到使用memcpy拷贝结构体
struct stu
{char name[20];int age;long id;
};
int main()
{struct stu s0 { xiaowang,20,10666 };struct stu s1 { xiaosong,40,10333 };memcpy(s0, s1, 8 * sizeof(char));memcpy(s0, s1, 7 * sizeof(int));printf(%s\n, s0.name);printf(%d\n, s0.age);printf(%ld\n, s0.id);return 0;
} 我这里只是举一个例子对于结构体的拷贝还需要学会结构体内存对齐如果有机会的话下次我会单独用一篇博客来分享一下关于结构体内存对齐的知识
②memcpy函数的模拟实现
不知道小伙伴们还记不记得我们之前学习过的qsort函数让我们顺便回顾一下吧qsort函数可以排序任意类型的数据函数底层使用的是快速排序的方法。那么qsort函数是怎么做到能够排序任意类型数据的呢没错就是在不知道数据类型的情况下直接交换每一个字节。
而我们的memcpy函数也差不多是这个意思我们最后传递的size_t类型参数就代表了需要替换的字节数所以我们可以传递一个size_t类的num代表字节数使用一个while循环每循环一次传递的num就减一直到num为0时循环结束此时正好交换了num个字节。
void* my_memcpy(void* arr0, const void* arr1, size_t num)
{assert(arr0 arr1);while (num--){*(char*)arr0 *(char*)arr1;arr0 (char*)arr0 1;arr1 (char*)arr1 1;}return (void*)arr1;
}
int main()
{int arr0[20] { 5,5,5,5,5 };int arr1[] { 1,2,3,4,5,6,7,8,9,10 };printf(替换前:\n);for (int i 0; i 5; i){printf(%d , arr0[i]);}my_memcpy(arr0, arr1, 5 * sizeof(int));printf(\n替换后:\n);for (int i 0; i 5; i){printf(%d , arr0[i]);}return 0;
}
那我们能不能尝试自己拷贝自己做到将arr1里的45678拷贝成它的前五位元素12345呢让我们尝试一下
void* my_memcpy(void* arr0, const void* arr1, size_t num)
{assert(arr0 arr1);while (num--){*(char*)arr0 *(char*)arr1;arr0 (char*)arr0 1;arr1 (char*)arr1 1;}return (void*)arr1;
}
int main()
{int arr0[20] { 5,5,5,5,5 };int arr1[] { 1,2,3,4,5,6,7,8,9,10 };printf(替换前arr1:\n);for (int i 0; i 10; i){printf(%d , arr1[i]);}my_memcpy(arr13, arr1, 5 * sizeof(int));printf(\n替换后arr1:\n);for (int i 0; i 10; i){printf(%d , arr1[i]);}return 0;
}
可以看到在我们想用arr1前方的元素拷贝给后方时却打印出了错误的答案这是为什么呢因为在对arr1进行拷贝的时候改变的是arr1本身当第一次拷贝时arr1中的4变成了1第二次拷贝时5变成了2第三次拷贝时6变成了3第四次拷贝时我们需要将7变成4可是此时arr1中的4已经被替换成了1所以此时7变成了1同理8也变成了2。这就说明了memcpy函数j仅仅是简单的从前往后进行拷贝并没有考虑内存有重叠的情况如果内存有重叠其行为是不确定的。
那么此时下一个要介绍的函数就该登场了~
二、memmove函数
可以看到memmove的参数与memcpy函数是一致的但与memcpy函数相较memmove函数能够完美的处理重叠的情况。
①memmove函数的使用
• 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
• 如果源空间和目标空间出现重叠就可以使用memmove函数处理。
那么我们使用memmove来证实上面的想法就用刚刚memcpy打印出错的代码
int main()
{int arr0[20] { 5,5,5,5,5 };int arr1[] { 1,2,3,4,5,6,7,8,9,10 };printf(替换前arr1:\n);for (int i 0; i 10; i){printf(%d , arr1[i]);}memmove(arr13, arr1, 5 * sizeof(int));printf(\n替换后arr1:\n);for (int i 0; i 10; i){printf(%d , arr1[i]);}return 0;
}
看来这次使用memmove确实解决了重叠的情况。
②memmove函数的模拟实现
想要实现memmove函数的模拟实现就需要我们解决上面memcpy函数的重叠问题。那么memcpy函数的重叠问题是如何造成的呢刚刚的思路我们可以用这张图来表示而我们按照memcpy的思路来进行拷贝就是从前往后拷贝用图片来表示出来应该是这样的这就是出现了重叠的情况我们想调用arr[3]时其指向的元素已经从4变成了1想要避免这种情况我们只需要在拷贝元素时保证需要拷贝的内容不被替换就好了。就比如此时我们转换一下思路我们现在想将12345拷贝成45678。这样的话能不能做到呢是可以的因为我们在将1拷贝成4后拷贝的位置和需要拷贝的内容的位置都向后挪一位此中重叠的元素为4和5但4和5在最开始就为1和2拷贝成功啦所以并不会发生错误的打印。这种情况就是拷贝内容初始位置在拷贝初始位置之后此时需要使用顺序拷贝。
而对于上面将45678变成12345我们可以尝试用逆序拷贝。通过这个图可以知道拷贝内容初始位置在拷贝初始位置之前此时需要使用逆序拷贝。通过逆序拷贝的方式就能够避免在拷贝前拷贝内容被替换掉的情况。
想要模拟实现memmove函数我们只需要将两种情况结合在一起先判断两个初始位置谁在前谁在后然后再对应的编写出解决方案就可以啦~拷贝内容初始位置在拷贝初始位置之后时直接使用memcpy的模拟函数就可以拷贝内容初始位置在拷贝初始位置之前时就逆序拷贝从需要拷贝的最后一个字节往前拷贝就能够解决了
void* my_memmove(void* arr0, const void* arr1, size_t num)
{assert(arr0 arr1);if (arr0 arr1){while (num--){*(char*)arr0 *(char*)arr1;arr0 (char*)arr0 1;arr1 (char*)arr1 1;}}else{while (num--)*((char*)arr0 num) *((char*)arr1 num);}return (void*)arr1;
}
int main()
{int arr0[20] { 5,5,5,5,5 };int arr1[] { 1,2,3,4,5,6,7,8,9,10 };printf(替换前arr1:\n);for (int i 0; i 10; i){printf(%d , arr1[i]);}my_memmove(arr1 3, arr1, 5 * sizeof(int));printf(\n替换后arr1:\n);for (int i 0; i 10; i){printf(%d , arr1[i]);}return 0;
}
这样就成功的模拟实现了memmove函数啦~
三、memset函数
memset的作用是复制字符 value (注意,不是字符串,是一个字符)到参数 ptr 所指向的字符串的前 num 个字符。
memset和前两个函数最大的区别就是前两个函数可以传输的是可以每一个的不同的可以改变的多样的。而memset能做到的只是将一个固定的字符拷贝到一段空间中。那么它的具体用法是什么呢我们来尝试一下使用memset将一段数组进行清零。
int main()
{int arr1[] { 1,2,3,4,5,6,7,8,9,10 };printf(清零前arr1:\n);for (int i 0; i 10; i){printf(%d , arr1[i]);}memset(arr1, 0, 10 * sizeof(int));printf(\n清零后arr1:\n);for (int i 0; i 10; i){printf(%d , arr1[i]);}return 0;
}
能够看到这边也是成功清零了那可能有人会问那不是也可以把值全部变成1全部变成2之类的吗那让我们来尝试一下这个思路欸怎么出现了这么一堆又奇怪又大的数字这是怎么一回事呢请仔细看刚刚的介绍我们传输的并不是直接的一个值而是一个将参数转化为二进制后填入的一个字节比如此时我们想填入的是1那么1转换为二进制形式就是0000 0001但是arr中存放的int整形变量有四个字节所以传进四个字节后其实这个对应的值会变成0000 0001 0000 0001 0000 0001 0000 0001。也就是一个非常大的数字。由于之前传递的0对应的就是00 00 00 00所以会直接是0并不是说明传递的是什么数字就会是什么数字想要理解memset函数就要清楚这一点。而传递其他的值大部分比较麻烦所以memset函数的主要作用还是使结构体或数组进行清零(注意并不是说就没有其他的作用了哦)。
四、memcmp函数 memcmp的作用是比较从ptr1和ptr2指针指向的位置开始向后的num个字节。
memcmp的用法与strncmp的用法很像都是用于指定长度的比较大小并且都是大于返回0的数小于返回0的数等于返回0。唯一不同的是memcmp函数是内存函数通过它能够比较所有的类型因为不论存储的内容是字符串整数还是其它类型的数据该函数都会逐个字节进行比较。
int main()
{int arr0[] { 1,2,3,4,5,6 };int arr1[] { 1,2,3,4,5,7 };if (memcmp(arr0, arr1, 6 * sizeof(int)) 0){printf(arr0arr1);}else if (memcmp(arr0, arr1, 6 * sizeof(int)) 0){printf(arr0arr1);}else{printf(arr0arr1);}return 0;
}
由此就可以进行两个数组的大小的比较。同样字符串也是可以的
int main()
{char arr0[] abcdef;char arr1[] abcdez;if (memcmp(arr0, arr1, 6 * sizeof(char)) 0){printf(arr0arr1);}else if (memcmp(arr0, arr1, 6 * sizeof(char)) 0){printf(arr0arr1);}else{printf(arr0arr1);}return 0;
}
那么关于比较常用的内存函数的知识就给大家分享到这里啦如果有讲解的有问题或者不清楚的地方还希望各位在评论区多多指出我也会吸取教训多多学习的那么我们下期再见啦~