【C进阶】内存函数

⭐博客主页:️CS semi主页
⭐欢迎关注:点赞收藏+留言
⭐系列专栏:C语言进阶
⭐代码仓库:C Advanced
家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我创作最大的动力,欢迎友友们私信提问,家人们不要忘记点赞收藏+关注哦!!!


前言

在之前讲解的关于字符串的拷贝我们已经知道了strcpy,strcmp,strstr等的处理字符串的库函数,但是现在有一个情况了,倘若是数字数组呢?数字字符进行拷贝,移动和比较是不是没有办法,对,这种方法叫做内存函数,所以今天我们要介绍的是memcpy,memmove,memcmp,memset这四个内存函数,对数组是很友好的!


一、memcpy

(一)介绍

在这里插入图片描述
根据图片我们能知道我们是根据字节的大小去决定拷贝过去的数,拷贝的方法是从src拷贝到dest中去。
我们先简单使用一下,如下代码:

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

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

	return 0;
}

在这里插入图片描述

(二)应用

那当然了,如果你想拷贝从3开始往后拷贝4个数(20个字节)的话那我们就直接变值即可,如下代码:

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

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

	return 0;
}

在这里插入图片描述
那我们拷贝17个字节呢?能不能拷贝过去呢?答案是可以的,可是发现下面的数据怎么拷贝了3,4,5,6,7过去呢?因为是数据在计算机中存放的是01 00 00 00小端存放,一直到7的位置存放的是07 00 00 00,所以取到了07,所以7能输出。
在这里插入图片描述

(三)模拟实现

那解释后上完整代码(后面赋代码):
在这里插入图片描述
根据画图我们知道,当我们想拷贝过去的时候,我们并不知道这个指针是什么类型的,是整型?是字符?那不确定我们看着个传参过去的是字节大小,所以我们就想到了强制类型转换成为char*指针一个字节一个字节往后找即可。

#include<stdio.h>
#include<assert.h>
//所以用void*
void* my_memcpy(void* dest, const void* src, size_t num) {
	assert(dest && src);
	//保存首元素地址
	void* ret = dest;
	while (num--) {
		//不知道是什么类型的指针,那就来char*一个字节一个字节去拷贝即可
		//一次强转是临时的
		*(char*)dest = *(char*)src;
		//强制类型转换
		dest = (char*)dest + 1;
		src = (char*)src + 1;
		/*++(char*)dest;
		++(char*)src;*/
	}
	return ret;
}

int main() {
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	//不确定arr2和arr1里面是什么类型的指针
	my_memcpy(arr2, arr1 + 2, 17);

	return 0;
}

(四)进阶:拷贝自己

那我们就有个比较大胆的想法,我们能不能实现一下在自身字符串处移动,我们按照原本的套路试一下:

#include<stdio.h>
#include<assert.h>
//所以用void*
void* my_memcpy(void* dest, const void* src, size_t num) {
	assert(dest && src);
	//保存首元素地址
	void* ret = dest;
	while (num--) {
		//不知道是什么类型的指针,那就来char*一个字节一个字节去拷贝即可
		//一次强转是临时的
		*(char*)dest = *(char*)src;
		//强制类型转换
		dest = (char*)dest + 1;
		src = (char*)src + 1;
		/*++(char*)dest;
		++(char*)src;*/
	}
	return ret;
}

void test1() {
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	//不确定arr2和arr1里面是什么类型的指针
	my_memcpy(arr2, arr1 + 2, 17);
}

void test2() {
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr + 2, arr, 20);

}

int main() {
	//test1();
	test2();
	return 0;
}

我们发现test2()函数看的样子很正确,可是真的这样吗?我们进入调试看一看吧!
在这里插入图片描述
这输出结果怎么是1 2 1 2 1 2 1 8 9 10,跟我们的预期结果1 2 1 2 3 4 5 8 9 10怎么差别那么大呢?我们画图解释一下:
在这里插入图片描述
发现当我们想要找替换3的时候,3已经被1覆盖了,只能复制1了,所以是错误的,那这种情况我们需要分情况讨论了:
以下情况是重叠的情况:
情况一:dest(被拷贝的数据)指针在src(源头数据)指针的左边时,需要从左往右拷贝,也就是从前往后拷贝。在这里插入图片描述

情况二:dest(被拷贝的数据)指针在src(源头数据)指针的右边时,需要从右往左拷贝,也就是从后往前拷贝。在这里插入图片描述

不重叠情况:从前往后拷贝和从后往前拷贝都可以。

那这么复杂的情况,早期的计算机编程师肯定想到过,所以就另外制作了一个库函数memmove来解决这种情况,那我们接下来介绍一下memmove库函数吧!

二、memmove

(一)介绍

首先的首先我们,先看一下这个函数在MSDN的介绍:
在这里插入图片描述
有了上面的概念和我们需要进行移动的想法,我们直接用memmove实现一下吧:

在这里插入图片描述
在这里插入图片描述
上面两串代码就是memmove的魅力,不管是从前往后还是从后往前,memmove都可以进行移动。

(三)模拟实现

既然有了上面的思路,那我们模拟实现就很轻松了,只要是dest在src左边的时候,那就是统一从前往后拷贝;dest在src右边的时候,那就是统一从后往前拷贝,注意,这里的dest和src是头指针:

#include<stdio.h>
#include<assert.h>
void* my_memmove(void* dest, const void* src, size_t num) {
	assert(dest && src);
	//保存首元素地址
	void* ret = dest;

	if (dest < src) {
		//从前向后拷贝
		while (num--) {
			//不知道是什么类型的指针,那就来char*一个字节一个字节去拷贝即可
			//一次强转是临时的
			*(char*)dest = *(char*)src;
			//强制类型转换
			dest = (char*)dest + 1;
			src = (char*)src + 1;
			/*++(char*)dest;
			++(char*)src;*/
		}
	}
	else {
		//从后向前拷贝
		while (num--) {
			//第一次往后跳了19个字节
			*((char*)dest + num) = *((char*)src + num);

		}
	}
	return ret;
}
void test3() {
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr, arr + 2, 20);

}

int main() {
	//test1();
	//test2();
	test3();
	return 0;
}

三、memcmp

(一)介绍

我们先看一下MSDN的介绍:
在这里插入图片描述
即;比较从ptr1和ptr2指针开始的num个字节。
那我们根据介绍进行制作一下这个吧!

在这里插入图片描述
如图所示,当我们在进行比较的时候,是根据字节大小比较的,所以这就取决于编译器是大端还是小端,根据字节在内存中的存放顺序进行比较的,我们知道,在VS环境下,是小端存储,也就是说5是0x05 00 00 00,03是0x03 00 00 00,所以取第一个字节,较大的是05,所以当字节数为不是4的倍数的时候,我们也可以比较:
在这里插入图片描述

(二)模拟实现

#include<stdio.h>
#include<assert>
int my_memcmp(const void* buf1, const void* buf2, size_t count) 
{
	assert(buf1 && buf2);
	//往后找字节进行比较
	while (count--) {
		if (*(char*)buf1 > *(char*)buf2) {
			return 1;
		}
		else if (*(char*)buf1 < *(char*)buf2) {
			return -1;
		}
		buf1 = (char*)buf1 + 1;//往后移动
		buf2 = (char*)buf2 + 1;
	}
	return 0;
}

int main() {
	int arr1[] = { 1,2,3 };
	int arr2[] = { 1,2,5 };
	int ret = my_memcmp(arr1, arr2, 9);
	printf("%d\n", ret);
	return 0;
}

四、memset

(一)介绍

名为内存设置函数,是以字节为单位来设置内存中的数据的。
在这里插入图片描述
如下所示,根据MSDN介绍我们进行书写:
在这里插入图片描述

(二)模拟实现

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

void* my_memset(void* dest, int c, size_t count)
{
	assert(dest);
	//头指针给存起来
	void* start = dest;
	//循环相赋值
	while (count--) {
		*(char*)dest = (char)c;
		dest = (char*)dest + 1;
	}
	return start;
}

int main()
{
	char arr[] = "hello world!";
	char* ret = my_memset(arr, 'x', 5);
	printf("%s\n", arr);

	return 0;
}

总结

当我们使用这些函数的时候,我们需要搞清楚这些函数的传参以及我们需要注意的事情,尤其是我们需要熟悉并掌握模拟实现这些内存函数的使用,这个是很关键的,对于自己是有很大的提升的!!!


客官,码字不易,来个三连支持一下吧!!!关注我不迷路!!!

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

2022horse

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

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

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

打赏作者

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

抵扣说明:

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

余额充值