C语言中的内存函数使用与模拟实现

目录

一、内存函数的使用

1、memcpy()函数

2、memmove()函数

3、memcpy()函数

4、memset()函数:

二、内存函数的模拟实现

1、模拟实现memcpy()函数

2、模拟实现memmove()函数


一、内存函数的使用

1、memcpy()函数

memcpy()函数可以指定字节数,把源空间的内容拷贝到目的空间中 ,第一个参数是目的空间,第二个参数是源空间,第三个参数是指定的字节数,该函数的参数和返回值均为void*,可以接收任意类型数据的地址,该函数返回值是目标空间的起始地址。

cpp 复制代码
//memcpy()函数
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int arr1[10] = { 0 };
	//想要把arr中的20个字节的数据拷贝到arr1中
	memcpy(arr1, arr, 20);
	for (int i = 0; i < 10; i++)
		printf("%d ", arr1[i]);
	printf("\n");

	float arr2[10] = { 3.14f, 2.13f, 9.16f, 7.98f, 6.07f, 0.29f };
	float arr3[10] = { 7.98f, 6.07f, 0.29f };
	memcpy(arr2, arr3, 20);
	for (int i = 0; i < 10; i++)
		printf("%lf ", arr2[i]);
	return 0;
}

注意:该函数拷贝同样要满足目标空间足够大,拷贝的字节数也不能乱指定。

2、memmove()函数

cpp 复制代码
//memmove()函数
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	memmove(arr + 2, arr, 20);
	for (int i = 0; i < 10; i++)
		printf("%d ", arr[i]);//打印结果为:1 2 1 2 3 4 5 8 9 10
	return 0;
}

memmove()函数的功能与memcpy()函数大致相同,只不过一个是可以原地拷贝,另一个是异地拷贝。

3、memcpy()函数

memcmp()函数是比较内存中指定字节内的数据。

cpp 复制代码
//memcmp()函数
int main()
{
	int arr1[10] = { 1, 2, 3, 4, 5 };
	int arr2[10] = { 2, 4, 6, 8, 10 };
	int arr3[10] = { 1, 2, 3, 4, 1 };
	int arr4[10] = { 1, 2, 3, 4, 5 };
	printf("%d\n", memcmp(arr1, arr2, 12));//打印结果为:-1
	printf("%d\n", memcmp(arr1, arr3, 20));//打印结果为:1
	printf("%d\n", memcmp(arr1, arr4, 20));//打印结果为:0
	//该函数的返回值与strcmp相同,如果前者小于后者,返回负数,否则返回正数,相等返回0
	return 0;
}

4、memset()函数:

cpp 复制代码
//memset()函数
int main()
{
	char str[] = "hello world!";
	memset(str, 'x', 5);//将字符串str的前5个字节设置为'x'
	printf("%s\n", str);//打印结果为:xxxxx world!
	return 0;
}

**memset()函数可以指定字节数来设置内存,设置的值可以自己指定。**使用该函数时,不能设置常量字符串,另外指定的是字节数,不是元素个数。

cpp 复制代码
//memset()函数
int main()
{
	int arr[10] = { 0 };
	memset(arr, 1, sizeof(arr));
	for (int i = 0; i < 10; i++)
		printf("%x\n", arr[i]);
	return 0;
}

上面代码中,将arr数组中的元素的每一个字节都设置成了1。

二、内存函数的模拟实现

1、模拟实现memcpy()函数

cpp 复制代码
//模拟实现memcpy()函数
void* my_memcpy(void* dest, const void* src, size_t size)
{
	assert(dest && src);
	void* ret = dest;
	while (size--)
	{
		//void*指针不能直接解引用和加减运算
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;//这样写比((char*)dest)++更加稳定
		src = (char*)src + 1;
	}
	return ret;
}

int main()
{
	int arr[10] = { 1, 2, 3, 4, 5 };
	int arr1[10] = { 10, 20, 30 };
	my_memcpy(arr, arr1, 12);
	for (int i = 0; i < 10; i++)
		printf("%d ", arr[i]);//打印结果为:10 20 30 4 5 0 0 0 0 0
	return 0;
}

下面演示自己设计的memcpy()函数在同一块空间的拷贝情况。

cpp 复制代码
//模拟实现memcpy()函数在同一块空间拷贝
void* my_memcpy(void* dest, const void* src, size_t size)
{
	assert(dest && src);
	void* ret = dest;
	while (size--)
	{
		//void*指针不能直接解引用和加减运算
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;//这样写比((char*)dest)++更加稳定
		src = (char*)src + 1;
	}
	return ret;
}

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	my_memcpy(arr + 2, arr, 20);//期望结果1 2 1 2 3 4 5 8 9 10
	for (int i = 0; i < 10; i++)
		printf("%d ", arr[i]);//打印结果为:1 2 1 2 1 2 1 8 9 10
	//出现这种原因的情况是因为内存重叠时候,使用memcpy()函数可能会出问题
	//那么如果在同一空间下拷贝,通常使用memmove()函数
	return 0;
}

C语言的memcpy()函数主要拷贝不重叠的内存,重叠的内存由memmove()函数拷贝,虽然在VS编译器上使用memcpy()函数可能也能拷贝内存重叠的情况,这是因为VS编译器对memcpy()函数功能实现与memmove()函数相同,但不是所有的编译器都这样。

2、模拟实现memmove()函数

cpp 复制代码
//模拟实现memmove()函数
void* my_memmove(void* dest, const void* src, size_t size)
{
	//使用memcpy()函数出现了内存覆盖,是因为将内存前面的数据拷贝到后面时,运用了从前向后拷贝
	//所以在实现memmove()函数时,如果将内存前面数据拷贝到后面,应该从后向前拷贝
	//如果是将内存后面数据拷贝到前面,应该从前向后拷贝,这就分成两种情况讨论
	//也就是dest<src时候,从前向后拷贝,dest>src时候,从后向前拷贝
	assert(dest && src);
	void* ret = dest;
	if (dest < src)//从前向后拷贝
	{
		while (size--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else//从后向前拷贝
	{
		while (size --)
			*((char*)dest + size) = *((char*)src + size);
	}
	return ret;
}

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	my_memmove(arr + 2, arr, 20);
	for (int i = 0; i < 10; i++)
		printf("%d ", arr[i]);//打印结果为:1 2 1 2 3 4 5 8 9 10
	return 0;
}
相关推荐
XuanRanDev6 分钟前
【构建工具】Gradle Kotlin DSL中的大小写陷阱:BuildConfigField
android·开发语言·kotlin
学途路漫漫7 分钟前
怎么修改node_modules里的文件,怎么使用patch-package修改node_modules的文件,怎么修改第三方库原文件。
开发语言·javascript·ecmascript
我命由我1234527 分钟前
34.Java 阻塞队列(阻塞队列架构、阻塞队列分类、阻塞队列核心方法)
java·服务器·开发语言·后端·架构·java-ee·后端开发
猿周LV30 分钟前
JUC (java. util.concurrent) 的常见类及创建新线程的方法等 [Java EE 初阶]
java·开发语言·java-ee
m0_7482489438 分钟前
猿创征文 【高级篇】Java 进阶之JVM实战
java·开发语言·jvm
计算机毕设指导642 分钟前
基于Springboot的小说网站【附源码】
java·开发语言·spring boot·后端·mysql·spring·maven
EPSDA1 小时前
Linux中的UDP编程接口基本使用
linux·运维·服务器·开发语言·c++·网络协议·udp
大乔乔布斯1 小时前
WSL,Power shell 和CMD, Git bash的区别
开发语言·git·bash
PEAK0LI1 小时前
C++知识点总结与复习
开发语言·c++
落樱弥城1 小时前
单入单出队列性能优化(Lock-Free)
开发语言·c++·性能优化·多线程