Copying类库函数的模拟实现 (2
一、memcpy
1.memcpy库函数的介绍
strcpy和strncpy都是针对字符进行的操作,有没有针对其他类型的copy函数呢?在这里我们引入memcpy库函数,它是对内存空间进行逐字节的拷贝,而不关心其中的数据类型。
memcpy有以下几个使用要点:
1.dest 参数要分配足够的空间,即大于等于 n 字节的空间。如果没有分配够空间,会出现溢出错误。
2.sou 参数所指向的数据大小应至少为 num 个字节,否则拷贝时会出现问题。
3.dest 和 sou 所指的内存空间不能重叠。
4.如果字符串中的内容包含’\0’则只能用 memcpy 进行拷贝,如果用 strcpy 则会被判断成结束的标志。
2.memcpy库函数的模拟思路及实现代码
在C++ Reference我们可以看到关于memcpy库函数的返回类型和参数:
void * memcpy ( void * destination, const void * source, size_t num );
由于未知数据类型,所以它的返回类型是void*,也就是目标的起始地址;参数分别为目标的起始地址,源字符串的起始地址,和需要拷贝的字节长度。
我们先来看一个调用memcpy库函数实现功能的代码
struct {
char name[40];
int age;
} person, person_copy;
int main ()
{
char myname[] = "Pierre de Fermat";
/* using memcpy to copy string: */
memcpy ( person.name, myname, strlen(myname)+1 );
person.age = 46;
/* using memcpy to copy structure: */
memcpy ( &person_copy, &person, sizeof(person) );
printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );
return 0;
}
运行结果如下:
person_copy: Pierre de Fermat, 46
下面我们进行对memcpy的模拟
void* my_memcpy(void* dest, const void* sou, size_t num)
{
char* ret = dest;
assert(dest != NULL);
assert(sou != NULL);
while (num--)//对字节进行拷贝
{
*((char*)dest) = *((char*)sou);
dest = (char*)dest + 1;
sou = (char*)sou + 1;
}
return ret;
}
int main()
{
int arr[20] = {0};
int p[10] = { 1, 2, 3, 4, 5, 6, 7 };
my_memcpy(arr, p, 5 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(arr + i));
}
return 0;
}
运行结果如下
可以看出我们只是对strcpy的代码进行了部分修改,因为memcpy是按照字节进行拷贝,所以我们要针对字节进行操作,将未知的类型指针强制转换为字符指针,进行逐字节的拷贝。
这样我们就完成了对库函数memcpy的模拟。
附上C++ Reference链接:memcpy C++ Reference
二、memmove
1.memmove库函数的介绍
memmove同memcpy库函数一样,都是对内存空间进行逐字节的拷贝,memove可以实现memcpy的功能,更重要的一点是,它可以处理源空间和目标空间出现重叠的拷贝问题。
memmove有以下几个使用要点:
1.dest 参数要分配足够的空间,即大于等于 n 字节的空间。如果没有分配空间,会出现数据溢出错误。
2.sou 参数所指向的数组的大小应至少为 num 个字节。
2.memmove库函数的模拟思路及实现代码
在C++ Reference我们可以看到关于memove库函数的返回类型和参数:
void * memmove ( void * destination, const void * source, size_t num );
与memcpy相似,它的返回类型是void*,也就是目标的起始地址;参数分别为目标的起始地址,源字符串的起始地址,和需要拷贝的字节长度。
我们先来看一个调用memcpy库函数实现功能的代码
int main ()
{
char str[] = "memmove can be very useful......";
memmove (str+20,str+15,11);
puts (str);
return 0;
}
运行结果如下
memmove can be very very useful.
现在我们开始对memmove库函数进行模仿
void* my_memmove(void* dest, const void* sou, size_t num)
{
char* ret = dest;
assert(dest != NULL);
assert(sou != NULL);
//下面进行从前往后拷还是从前往后考的判断
if (dest <= sou)
{//由于目标的地址在源地址前,所以从前往后拷不会覆盖
while (num--)//对字节进行拷贝
{
*((char*)dest) = *((char*)sou);
dest = (char*)dest + 1;
sou = (char*)sou + 1;
}
}
else
{//目标地址在源地址后,从后往前拷不会覆盖
while (num--)
{
*((char*)dest + num)= *((char*)sou+num);
}
}
return ret;
}
int main()
{
char str[] = "memmove can be very useful!!!!!!";
my_memmove(str + 20, str + 15, 11);
printf("%s\n", str);
return 0;
}
运行结果如下
代码的关键部分就是判断是将字节从前向后拷贝还是从后向前拷贝,即
if (dest <= sou)
{//由于目标的地址在源地址前,所以从前往后拷不会覆盖
while (num--)//对字节进行拷贝
{
*((char*)dest) = *((char*)sou);
dest = (char*)dest + 1;
sou = (char*)sou + 1;
}
}
else
{//目标地址在源地址后,或其他无覆盖的情况,从后往前拷不会覆盖
while (num--)
{
*((char*)dest + num)= *((char*)sou+num);
}
}
我们画图对两种有覆盖的情况进行解释
第一种
dest在sou前:从前向向后拷贝
如果从后向前拷贝,则会导致5位置的数据被8位置的数据覆盖,造成1位置最终错误的得到8的数据。
第二种
dest在sou后:从后向前拷贝
如果从前向后拷贝,会将3位置的数据被1位置的数据覆盖掉,最终5位置错误地得到1位置的数据。
第三种
无覆盖的情况:
以上就是对memmove的模拟实现。
附上C++ Reference链接:memmove C++ Reference
结合上一篇博客 String中Copying类库函数的模拟实现(1)我们就实现了对String头文件中所有Copying类型的库函数的模拟。
希望以上内容对您的学习有所帮助。