【模拟实现C语言库函数】string.h中的内存操作函数

35 篇文章 1 订阅

相关文章

  1. 【C语言】数据在内存中是以什么顺序存储的?
  2. 【C语言】整数在内存中如何存储?又是如何进行计算使用的?
  3. 【C语言】利用void*进行泛型编程
  4. 【C语言】4.指针类型部分

使用内存库函数实际上要包含string.h头文件,这个大伙要注意。

1. 模拟 memcpy 内存拷贝

两个指针的指向必须是两块互相独立的内存区域,即两个不同的数组。
dest空间必须比src空间大;
bytes表示要从src拷贝到dest的字节数。

// void* 通用的泛型编程,可以接收任何指针
void* my_memcpy(void* dest, const void* src, size_t bytes) {
	assert(dest && src);
	if (dest == src) {
		return dest;
	}
	void* t = dest;
	while (bytes--) {
		// 不清楚void*接收的是什么类型指针,直接char*一个个字节拷贝。
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
		/*
			((char*)dest)++;
			((char*)src)++;
			---------------
			++((char*)dest);
			++((char*)src);
			这两种写法换成c++都不行
		*/
	}
	return t;
}
int main() {
	int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
	int arr2[] = { 1, 2, 3, 4};
	my_memcpy(arr1 + 4, arr2, sizeof(int) * 4);
	for (int i = 0; i < 8; i++) {
		printf("%d ", arr1[i]);
	}
	return 0;
}

在这里插入图片描述

对于标准的memcpy,如果不进行完善实际上是有问题的:假设有一个数组 int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 },指针dest和指针src两个指针指向同一个数组内存,并且以字节为单位进行内存移动,会出现几种情况:

1.dest和src指向同一个地址,如my_memcpy(arr, arr, 4),等于啥也没动;

2.dest指向地址值大于src
 2.1 如my_memcpy(arr + 4, arr, 16),也就是将5 6 7 8改成1 2 3 4,结果为1 2 3 4 1 2 3 4;
 -----------------------------------------------------
 2.2 如my_memcpy(arr + 2, arr, 16),也就是将3 4 5 6改成1 2 3 4,即 1 2 1 2 3 4 7 8
 但结果会变成1 2 1 2 1 2 7 8,因为原来3 4位置被改成了1 2。

3.dest指向地址值小于src
 3.1 如my_memcpy(arr, arr + 4, 16),也就是将1 2 3 4改成5 6 7 8,结果为5 6 7 8 5 6 7 8;
 -----------------------------------------------------
 3.2 如my_memcpy(arr, arr + 2, 16),也就是将1 2 3 4改成3 4 5 6,结果为 3 4 5 6 5 6 7 8。

到这里会发现,也就只有2.2的结果不是我们想要的,这是因为自己实现的memcpy并不不完善,如果是string.h库函数中的memcpy则不存在这个问题。
在这里插入图片描述
本来是要将3 4 5 6改成1 2 3 4,结果改成了1 2 1 2。

事实上对于memcpy函数,C语言标准定义的是两个指针指向的内存位置不能是同一块区域,但显然对于vs2022的编译器而言是将memcpy完善了。但我们使用时还是尽量不要将两个指针指向同一个数组内的元素地址,毕竟要考虑到其它编译器并不一定完善。

画图分析2.2 my_memcpy(arr + 2, arr, sizeof(int) * 4):

在这里插入图片描述
图中每个格子代表arr数组中的一个元素,每个元素四个字节。我们利用这个简单的图分析上面模拟实现memcpy的代码,不难看出实际上拷贝是从前往后进行拷贝的,也就是从src、dest的起始位置开始往后拷贝。当拷贝完8个字节后,就变成了下面的样子:
在这里插入图片描述
这时的3和4早已被拷贝成了1和2,3和4不存在了。那么5和6就无法被拷贝成3和4了,自然而然也变成了1和2。

而解决这个问题,使用库函数memmove最好,对于这个函数,C语言的使用标准是这样的:两个指针既可以指向同一块内存区域,也可以像memcpy一样,两个指针指向不同内存区域。

2. 模拟 memmove 内存移动

如果自己实现memmove,通过上面例子出现的问题(同一块数组内存区域),如果要解决该问题,要考虑到的情况实际上也就是从前往后还是从后往前拷贝的问题,这个得由dest和src的地址大小比较后决定。

就对于上面模拟实现memcpy的问题,如果是从后往前拷贝,比如把6改成4,再把5改成3,再把3改成2,把2改成1,互不影响那么问题迎刃而解,但如果大伙认为真这么简单那就打错特错了。

对于内存而言以字节为单位,1个整型4个字节,我们实际上是要从最后一个字节开始往前一个个字节拷贝。arr数组的内存布局如下:
在这里插入图片描述
每一格都是1个字节,四格则构成一个完整的整型数据,也就是arr数组中的一个元素。格子中的值是用十六进制表示的,至于为什么是倒着存储的,这是因为当前机器以小端字节序存储数据(详细了解请看本篇文章最上面的 相关文章位置,第一个链接中的文章有解释)。

则对于dest地址值大于src的情况,可以这样拷贝:
在这里插入图片描述
而对于dest地址值小于src的情况,照常从前往后拷贝:
在这里插入图片描述

//模拟memmove(两个指针的指向可以是两块互相独立内存,也可以是同一块内存)
void* my_memmove(void* dest, const void* src, size_t bytes) {
	assert(dest && src);
	void* t = dest;
	// 从后面最后一个字节往前,将6改成4,再将5改成3,4改成2,3改成1,解决上面memcpy 2.2中的问题
	if (dest > src) {
		while (bytes--) { 
			*((char*)dest + bytes) = *((char*)src + bytes);
		}
	} 
	else if (dest < src) {     
		while (bytes--) {
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	} 
	return t;
}

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

在这里插入图片描述
成功把3 4 5 6改成1 2 3 4!


在这里插入图片描述
把1 2 3 4改成3 4 5 6也没问题!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

念来过倒字名qwq

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

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

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

打赏作者

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

抵扣说明:

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

余额充值