今天我来讲一下我最近学习的一些函数知识:
一.memcopy的使用及模拟实现
二.memmove的使用及模拟实现
三.memset的使用
四.memcmp的使用
1.memcopy的使用及模拟实现
同样,我们看一下这个函数的基本参数:
这个函数的各种参数,返回类型都是void*,说明这个函数应该能用所有的函数类型,这个时候我们要好好的注意1了。
这个函数能够确定返回多少个字节,看个人需求,下面我们来一起使用一下:
#include<stdio.h>
#pragma warning(disable:4996)
#include<ctype.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };//待被改变的数组
int arr2[10] = { 2,4,1,3,5,9,0,6,8,7 };//进攻方
memcpy(arr1,arr2,20);
int i = 0;
for (i = 0; i < sizeof(arr1) / sizeof(arr1[0]);i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
我们看到,这个数组成功的被改动了,我们也迫不及待的来模拟实现一下吧:
void* my_memcpy(void* destination, const void* source,size_t num)//以前我们碰到过,最好是一个一个字节进行交换
{
while (num)//没有发生交换就继续发生交换
{
*(char*)destination = *(char*)source;//注意是内容交换而不是指针交换
((char*)destination)++;
((char*)source)++;//由于强制类型转换具有临时性,我们要多多用上小括号来确保它能达到我们的目的
num--;//每次交换一个字节就要去减一个,那么我们要去强制类型转换成char*类型
}
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };//待被改变的数组
int arr2[10] = { 2,4,1,3,5,9,0,6,8,7 };//进攻方
my_memcpy(arr1,arr2,20);
int i = 0;
for (i = 0; i < sizeof(arr1) / sizeof(arr1[0]);i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
这样就比较完美了。
(注意:中间的((char*)destination)++;
((char*)source)++;还可以写成destination=(char*)destination+1;source=(char*)source+1,效果相同;因为临时性,没有经过强制类型转换的指针不能+或-1操作,我们要留意)
2.memmove的使用及模拟实现
同样的,我们来看一下这个函数的基本情况:
这个函数和上面的memcopy最大的区别是这个memmove可以在内存重叠的地方进行数据移动,而memcopy最好不要去应用内存重叠的区域,要不然容易发生错误。
我们先来个错误示例:(单纯使用memcpy函数,来对比memmove)
#include<stdio.h>
#pragma warning(disable:4996)
#include<ctype.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
void* my_memcpy(void* destination, const void* source,size_t num)//以前我们碰到过,最好是一个一个字节进行交换
{
while (num)//没有发生交换就继续发生交换
{
*(char*)destination = *(char*)source;//注意是内容交换而不是指针交换
((char*)destination)++;
((char*)source)++;//由于强制类型转换具有临时性,我们要多多用上小括号来确保它能达到我们的目的
num--;//每次交换一个字节就要去减一个,那么我们要去强制类型转换成char*类型
}
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };//待被改变的数组
//int arr2[10] = { 2,4,1,3,5,9,0,6,8,7 };//进攻方
my_memcpy(arr1+1,arr1,20);
int i = 0;
for (i = 0; i < sizeof(arr1) / sizeof(arr1[0]);i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
看结果:
为什么失败了呢?主要是因为内存重叠,操作不当。
下面我们用画图的方式为大家讲解:
(注意:蓝色框代表“source”,红色框代表“destination”)
按照从左往右,从上到下的顺序,这样就可以很好的解释为什么会打印出这么多‘1’了。
当然我们还有一种方法来避免,只需要把arr1和arr1+1这两个指针交换一下位置就好了,我们看:
#include<stdio.h>
#pragma warning(disable:4996)
#include<ctype.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
void* my_memcpy(void* destination, const void* source,size_t num)//以前我们碰到过,最好是一个一个字节进行交换
{
while (num)//没有发生交换就继续发生交换
{
*(char*)destination = *(char*)source;//注意是内容交换而不是指针交换
((char*)destination)++;
((char*)source)++;//由于强制类型转换具有临时性,我们要多多用上小括号来确保它能达到我们的目的
num--;//每次交换一个字节就要去减一个,那么我们要去强制类型转换成char*类型
}
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };//待被改变的数组
//int arr2[10] = { 2,4,1,3,5,9,0,6,8,7 };//进攻方
my_memcpy(arr1,arr1+1,20);
int i = 0;
for (i = 0; i < sizeof(arr1) / sizeof(arr1[0]);i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
这样就可以很好的避免这个问题了。
我们不妨画一下图来解释:
按照从左到右,从上到下的顺序,是不是很清晰?
这样我们就可以在my_memcpy的基础上进行改造了,我们看下面:
#include<stdio.h>
#pragma warning(disable:4996)
#include<ctype.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
void* my_memmove(void* destination, const void* source,size_t num)//以前我们碰到过,最好是一个一个字节进行交换
{
if (source > destination)
{
while (num)//没有发生交换就继续发生交换
{
*(char*)destination = *(char*)source;//注意是内容交换而不是指针交换
((char*)destination)++;
((char*)source)++;//由于强制类型转换具有临时性,我们要多多用上小括号来确保它能达到我们的目的
num--;//每次交换一个字节就要去减一个,那么我们要去强制类型转换成char*类型
}
}
else if (source < destination)
{
while (num)
{
char* des = (char*)destination + num-1;
char* sou = (char*)source + num-1;//注意-1的操作啊,不然会有bug的,末尾必记住
*(des) = *(sou);
des--;
sou--;
num--;//每次交换一个字节就要去减一个,那么我们要去强制类型转换成char*类型
}
}
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };//待被改变的数组
//int arr2[10] = { 2,4,1,3,5,9,0,6,8,7 };//进攻方
my_memmove(arr1+1,arr1,20);
int i = 0;
for (i = 0; i < sizeof(arr1) / sizeof(arr1[0]);i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
我们只是把source和destination的顺序交换一下:
还不错,顺便就把memmove的模拟实现完成了。
对于使用memmove函数而言,把my_memmove换成memmove,结果是一样的。
3.memset的使用
还是惯例,我们先简单看一下它的基本信息:
这个函数的用法是给一个指针,指针以及其往后的内容要改变num个字节,这些内容均改变成value。
为了便于理解,我们直接上代码:
#include<stdio.h>
#pragma warning(disable:4996)
#include<ctype.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
int main()
{
char arr1[100] = "hellobiteu";
printf("%s\n",memset(arr1, 'x', 6));
return 0;
}
这里为什么可以认为‘x’是整型呢?因为‘x’是以ascii的码值存放在内存里面的,如果认为‘x’是字符,那么用int打印成功的原因是int类型的范围远远大于char类型。
(注意:我们最好不要用这个函数去处理char类型以外的数组,如int元素组成的数组,它是一个字节一个字节进行改变的,容易出现bug,我们可以在调试过程中打开内存布局,好好观察一下)
4.memcmp的使用
我们先看一下这个函数的基本信息:
这个函数和我们之前的strncmp很像,传参的样式几乎相同,但这个涉及到内存方面,而且是一个一个字节进行比较,理论上是可以任意类型的数据内存进行比较的,字符(串)的比较当然是没有问题的,但事实真的是如此吗?
这里我们仅展示比较字符串的,比较整型的bug我们在看到下一篇文章就自然而然就懂了。
#include<stdio.h>
#pragma warning(disable:4996)
#include<ctype.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
int main()
{
char arr1[100] = "hellobit";
char arr2[100] = "hellobiteu";
printf("%d", memcmp(arr1, arr2, 8));
return 0;
}
下一篇再见!