冰冰学习笔记:内存操作函数

在前面的章节中我们介绍了字符操作函数的用法,用以实现字符串的复制,连接,比较,查找等操作。但是C语言中并非只有字符串需要这些操作,其他类型的变量也会用到复制,比较等操作。此时,字符串操作函数将不再适用这些类型的操作,所以我们需要内存处理函数进行此类操作。此类函数和strn带头的函数不同,他们在遇到NUL字节时不会停止操作。

此类函数的头文件为<string.h>

一、memcpy库函数

1.1函数介绍:

memcpy函数含有三个参数。

第一个参数为目标空间,即存放拷贝过来的字节内容的起始地址,为void* 类型的指针变量

第二个参数为源头空间,即需要拷贝的字节内容的起始地址,也为void* 类型的指针变量

第三个参数为size_t类型的变量 count,其代表拷贝的字节数

返回类型为 void* 的指针变量,在使用时可以强制类型转换为需要使用的指针类型。

为什么都是void*类型呢?

因为void*类型的指针可以接收任何类型的变量,方便memcpy函数对任何类型的数据进行处理操作。这点与qsort函数类似。

所以该函数的作用为:从src的起始位置复制count个字节到dest的内存起始位置,可以复制任何类型的值。

使用案列:

该例子表明用memcpy函数将arr2中的前两个字节复制放到arr1中。

因此打印出来为:abxxxxxxxx 

memcpy不仅可以用于字符串,也可用于其他类型的变量

该例子表明将arr2中的24个字节复制到arr1中,我们发现,arr2中并没有这么多的字节可供拷贝,会发生什么呢?

我们发现,memcpy函数不仅将arr2中存在的20个字节全部复制过去,还把arr2后面存放的不属于数组arr2的内容依旧复制到了arr1中,这也就说明,memcpy函数复制的字节数完全取决于count指定的字节数目。

1.2函数模拟:

下面对该函数进行模拟实现

memcpy函数的参数类型为void*类型,我们无法对其进行相关的操作,因此我们如果要对其进行操作必须将其强制类型转换为其他类型的指针来操作。

转换为什么类型呢?我们发现,由于count为操作的字节个数,所以转换为char*类型的指针可以对其进行逐字节访问。

现在问题就迎刃而解了。

void*  my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;//存放起始地址
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

memcpy函数还存在一个问题:memcpy函数无法处理重叠空间的拷贝。

int arr2[10]={1,2,3,4,5,6,7,8,9,10};

现在我想将arr1中的12345,拷贝放到34567的位置,此时就会出现空间重叠了,memcpy函数无法完成(有的编译器下的memcpy函数可以实现)。我们模拟实现的版本也是无法完成此类操作的。

如果想完成此类操作就必须使用 memmove函数来实现

二、memmove库函数

2.1函数介绍:

 我们发现memmove函数的参数和返回类型与memcpy函数完全相同。

确实如此,memmove函数可以理解为功能更完善的memcpy函数,它能完成memcpy函数的一切功能,而且支持重叠空间的字符拷贝。

此时我们在将arr2中的元素12345,放到34567的位置试验一下

此时目的达到了。

下面我们来分析一下原因,为什么memcpy做不到,memmove能做到

 

memcpy函数在进行复制的时候,先将1放到3,在将2放到4,然后将3放到5,可是此时3中存放的还是3吗?显然不是,此时存放的已经是被复制过来的1了,所以5中存放的是1而不是3,同理,6中存放的也不是4而是覆盖后的2 ……

所以memcpy函数无法进行重叠空间的复制。

memmove函数为什么就可以实现呢?

memmove函数在这中情况下很有可能是用的从后向前的复制。

 先把5放到7,在把4放到6,然后把3放到5,再把2放到4,最后把1放到3,此时就不会覆盖元素了。

但是这并不代表memmove总是使用从后向前的复制方法,例如将arr2中的34567放到12345中,从后向前复制就会引起元素覆盖,就必须使用从前向后复制。

所以memmove函数会根据情况来选定使用哪种方式进行复制。

2.2函数模拟:

现在我们开始模拟实现memmove函数,有了上面的分析,我们现在只需要解决一个难题就可以进行实现,那就是如何来断定使用从前向后的复制,还是使用从后向前的复制。

根据分析我们发现,当源头指针src指向的位置小于目标dest指针指向的位置时,需要采用从后向前的复制,当源头指针src指向的位置大于dest指向的位置时,需要从前往后进行复制。

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (src<dest)
	{
		//从后向前
		while (count--)
		{
			*((char*)dest + count) = *((char*)src + count);
			//count在不断变小,dest和src将会不断向前移动
		}
	}
	else
	{
		//从前往后
		while (count--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	return ret;
}

三、memcmp库函数

3.1函数介绍:

该函数含有三个参数,void*类型的buf1,void*类型的buf2,size_t类型的count

该函数的作用是对两段内存中的内容进行比较,这两段内存分别起始于a和b,共比较count个字节,这些值按照无符号类型逐字节进行比较,返回类型和strcmp函数一样,负值表示a小于b,正值表示a>b,零表示a=b。

由于这些值是根据一串无符号字节进行比较,如果用于比较不是单字节的数据,就可能给出不可预料的结果。

该例子使用memcmp对arr1和arr2的前17个字节进行比较,结果将返回0,二者相等

如果count改为18结果将返回负数,因为第18个字节arr1为00,arr2为33。

3.2函数模拟:

该函数的模拟实现是比较简单的,依旧使用while循环来判断相等的字符,当字符不在相等或者count为0时跳出。

跳出后进行判断是否为count个字节完全比对完成而结束,如果是,则说明两个内存空间时相同的,返回0;

如果不是,则说明是因为字符不同而跳出循环,返回两字节的差值。

int my_memcmp(const void* buf1, const void* buf2, size_t count)
{
	assert(buf1 && buf2);
	while (count&&(*(char*)buf1==*(char*)buf2))
	{

		count--;
		buf1 = (char*)buf1 + 1;
		buf2 = (char*)buf2 + 1;
	}
	if (count == 0)//判断是否为count个字节比完
	{
		return 0;
	}
	return *(char*)buf1 - *(char*)buf2;

}

四、memset库函数

memset库函数含有三个参数,void* 类型的dest,指向被操作空间的起始地址,int c 希望被设置的字符值,size_t count 设置的字节个数。

其作用为,将dest所指向的空间的count个字节设置为字符值c

将数组arr1中的数据12个字节全部设置为0

 结果:

五、memchr库函数

 memchr库函数含有三个参数,void*类型的buf,指向的是需要查找的内存空间起始地址,int c 要查找的字符,size_t count,查找的字节数量。

该函数的作用为从buf的起始位置开始查找字符c第一次出现的位置,并返回一个指向该位置的指针,共查找count个字节,如果在count个字节中没有找到,则返回空指针。

该例子表明从arr中寻找前8个字节中第一次出现 'o' 的位置,然后将后面的字符打印出来,因此打印的字符应为"o world!"

  • 19
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bingbing~bang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值