C语言学习--String中Copying类库函数的模拟实现(2)(包含对memcpy,memmove库函数的介绍,要点分析及模拟示例代码)

一、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类型的库函数的模拟。
希望以上内容对您的学习有所帮助。

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值