随笔——C语言内存函数

C语言内存函数


memcpy

之前我们学习了字符串拷贝函数strcpy,今天我们学习内存拷贝函数memcpy,它的头文件也是string.h

	 void * memcpy ( void * destination, const void * source, size_t num );
  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置
  • 这个函数在遇到 ‘\0’ 的时候并不会停下来,它只依靠num停下
  • 如果按照C标准来说,若是source和destination有任何部分重叠,复制都会出错;但实际上对于笔者所用的这版VS,即使内容有重叠也可以正常使用(但我可没鼓励过你们有重叠也用memcpy)

memcpy的使用方法及模拟实现都与strcpy极其相似,实际上,完全可以用memcpy代替strcpy;
代码如下:

#include<stdio.h>
#include<string.h>
#include<assert.h>

void* my_memcpy(void* destination, const void* source, size_t num)
{
	assert(destination && source);
	void* ret = destination;
	while (num--)
	{
		*((char*)destination) = *((char*)source);
		(char*)destination = (char*)destination + 1;
		//还有种不推荐的写法:((char*)source)++;用括号强制改变操作符结合性
		(char*)source = (char*)source + 1;
	}
		return ret;
}

int main()
{
	int i = 0;
	int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1, 5 * sizeof(int));
	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

注意这段代码:

while (num--)
{
	*((char*)destination) = *((char*)source);
	(char*)destination = (char*)destination + 1;
	//还有种不推荐的写法:((char*)source)++;用括号强制改变操作符结合性
	(char*)source = (char*)source + 1;
}

这段代码的运行顺序是(为方便表述,这里定义num初始值为3):

  • 先使用,num==3;为真;减减变成2;执行循环内容
  • 先使用,num==2;为真;减减变成1;执行循环内容
  • 先使用,num==1;为真;减减变成0;执行循环内容
  • 先使用,num==0;为假;减减变成-1;跳出循环

memmove

它的头文件也是string.h

void * memmove ( void * destination, const void * source, size_t num );
  • 和memcpy的差别就是memmove函数处理的源内存块和⽬标内存块是可以重叠的
  • 如果源空间和⽬标空间出现重叠,就得使⽤memmove函数处理,当然不重叠也可以用

为什么刚才我们写的my_memcpy不能用于对重叠内存块进行拷贝呢?就拿下面代码为例:

#include<stdio.h>
#include<string.h>
#include<assert.h>

void* my_memcpy(void* destination, const void* source, size_t num)
{
	assert(destination && source);
	void* ret = destination;
	while (num--)
	{
		*((char*)destination) = *((char*)source);
		(char*)destination = (char*)destination + 1;
		//还有种不推荐的写法:((char*)source)++;用括号强制改变操作符结合性
		(char*)source = (char*)source + 1;
	}
	return ret;
}

int main()
{
	int i = 0;
	int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int arr2[10] = { 0 };
	my_memcpy(arr1 + 2, arr1, 5 * sizeof(int));
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

调试控制台结果:
调试控制台结果
注:以下的每次改变是以int为单位的,箭头的名字只是用于分辨哪个是源哪个是目标而已,不是说指向的位置就是arr1和arr1 + 2

刚开始:
在这里插入图片描述
第一次改变:
在这里插入图片描述
第二次改变:
在这里插入图片描述
第三次改变:
在这里插入图片描述
第四次改变:
在这里插入图片描述
第五次改变:
在这里插入图片描述
可以看到,在拷贝的过程中,源的内容被破坏了,因此拷贝出错

知道了错在哪,再写my_memmove就简单一些了,只要让源先拷贝过去在改变就行了,就比如刚刚的这五次改变,从前往后拷贝会出问题,那从后往前拷贝呢?
这样的话:
初始位:
在这里插入图片描述

第一次:
在这里插入图片描述
第二次:
在这里插入图片描述
第三次:
在这里插入图片描述
第四次:
在这里插入图片描述
第五次:
在这里插入图片描述
但从后向前拷贝适用于一切重叠情况吗?
当然不是,比如现在把实参改成:

my_memcpy(arr1, arr1 + 2, 5 * sizeof(int));

那从后往前拷贝反而会出问题,而从前向后拷贝则正常
所以花半天图得出的最后结论是要写个分支结构,重叠情况不同,拷贝方法也不同
可以分为三种情况:

  • 不重叠:从前往后和从后往前都可以,那个方便用哪个
  • 重叠1:从前往后拷贝
  • 重叠2:从后往前拷贝

接下来就是要找到这两种重叠的边界条件了:
我们很容易得到:当dest处于这个区间(sour < dest < (char*)sour + num)是要从后往前拷贝的:

在这里插入图片描述
当(char*)dest + num处于这个区间(sour < (char*)dest + num < (char*)sour + num)的时候是从前往后拷贝的:
在这里插入图片描述

memmove的实现:
注意:千万不要把我们刚刚总结出来的区间直接往if()里填,我第一次就是直接往里填,出错才想到C语言里没有区间,比如a<b<c是先判断a<b是真是假,再拿这个真或者假去和c比较

#include<stdio.h>
#include<string.h>
#include<assert.h>

 //第一版:sour < dest < (char*)sour + num
void* my_memmove_1(void* destination, const void* source, size_t num)
{
	assert(destination && source);
	void* ret = destination;
	if (source < destination && destination < (char*)source + num)
	{
		while (num--)
		{
			*((char*)destination + num) = *((char*)source + num);
		}
	}
	else
	{
		while (num--)
		{
			*((char*)destination) = *((char*)source);
			(char*)destination = (char*)destination + 1;
			(char*)source = (char*)source + 1;
		}
	}
	return ret;
}

//第二版:sour < (char*)dest + num < (char*)sour + num
void* my_memmove_2(void* destination, const void* source, size_t num)
{
	assert(destination && source);
	void* ret = destination;
	if (source < (char*)destination + num && (char*)destination + num < (char*)source + num)
	{
		while (num--)
		{
			*((char*)destination) = *((char*)source);
			(char*)destination = (char*)destination + 1;
			(char*)source = (char*)source + 1;
		}

	}
	else
	{
		while (num--)
		{
			*((char*)destination + num) = *((char*)source + num);
		}
	}
	return ret;
}

int main()
{
	int i = 0;
	int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int arr2[10] = { 0 };

	//memmove(arr1 + 2, arr1, 5 * sizeof(int)):      1 2 1 2 3 4 5 8 9 10    
	//memmove(arr1, arr1 + 2, 5 * sizeof(int)):      3 4 5 6 7 6 7 8 9 10
	my_memmove_2(arr1, arr1 + 2, 5 * sizeof(int));
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

memset

它的头文件也是string.h

void * memset ( void * ptr, int value, size_t num );

memset是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。
我曾经疑惑第二个形参为什么是int,后来问了AI,得到的结果是:
在这里插入图片描述
这个函数没什么好说的,就是以字节为单位批量赋值

#include<stdio.h>
#include<string.h>

int main()
{
	char str[] = "hello word";
	memset(str, 'x', 7);
	printf("%s\n", str);
	return 0;
}

调试控制台结果:
在这里插入图片描述
但也是因为是以字节为单位批量赋值,所以如果对一个int数组赋值,要稍微注意一下
比如将一个int数组全初始化为1:

#include<stdio.h>
#include<string.h>

int main()
{
	int arr[10] = { 0 };
	int i = 0;
	memset(arr, 1, 10 * sizeof(int));
	for (; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

这样写肯定会出问题,调试控制台结果:
在这里插入图片描述
因为它是字节为单位,而非元素为单位:
在这里插入图片描述
在这里插入图片描述


memcmp

它的头文件还是string.h

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
  • ⽐较从ptr1和ptr2指针指向的位置开始,向后的num个字节
  • 返回值如下:

在这里插入图片描述
直接上代码:

#include<stdio.h>
#include<string.h>

int main()
{
	int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
	int arr2[] = { 1, 2, 3, 4, 8, 8, 8, 8 };
	int ret = memcmp(arr1, arr2, 5 * sizeof(int));
	printf("%d\n", ret);
	return 0;
}

调试控制台结果:
在这里插入图片描述
在这里插入图片描述

我们老师说比较顺序是:01比01,00比00,00比00,00比00……而不是00比00,00比00,00比00,01比01
为什么呢?似乎是我还没学到的知识,在此先埋个坑,应该会在下篇博客得到解决。


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值