【C语言】内存函数

发布于:2025-04-07 ⋅ 阅读:(40) ⋅ 点赞:(0)

大家好,很高兴又和大家见面了!!!

在C语言标准库中,有一些直接对内存进行操作的函数,我们将其称之为内存函数,这些函数位于头文件<string.h>,在网站https://cplusplus.com/reference/cstring/https://cplusplus.com/https://cplusplus.com/reference/cstring/中我们可以看到这些函数:(ps:这个链接是C语言官网,大家可以打开看一下,十分的详细,包括函数原型等等)

一·必备头文件

所有内存操作函数都声明在<string.h>头文件中,使用前请确保包含:

#include <string.h>

二·五大内存操作函数详解 

1. memcpy - 内存复制专家

1.1函数原型

void* memcpy(void* dest, const void* src, size_t n);

1.2功能:将src指向的地址开始的n个字节,原样复制到dest指向的地址。

  • 函数有三个参数:第一个参数为拷贝的目的地地址,第二个参数为拷贝的源对象的地址,第三个参数为拷贝的大小,单位为字节。
  • 在拷贝的过程中,函数不会受终止符的影响,只会根据字节数量n来进行拷贝;
  • 在拷贝前需要注意,目的地的空间大小和源对象的空间大小都应该至少是n个字节,并且拷贝的目标空间与源空间不能有重叠。

这里提出一个问题,如下:

碰到如下的代码,如何将arr1中的代码拷贝的arr2中?

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6 };
	int arr2[10] = { 0 };

}

1.3 可以考虑运用下标的方法来进行拷贝,如下:

先将arr1中的值赋给arr2,在进行打印。

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6 };
	//             0 1 2 3 4 5
	int arr2[10] = { 0 };
	for (int i = 0; i < sizeof(arr1) / sizeof(arr1[0]); i++)
	{
		arr2[i] = arr1[i];
	}
	
	for (int i = 0;i < 6;i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;

}

 

1.4 strcpy函数

这时可能就有同学想到strcpy函数来进行拷贝了,nonononono!!!!

strcpy函数是字符函数,只能用来处理字符,字符串,这里是数组,所以不可以的。

 1.5memcpy函数

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1, 24);
	for (int i = 0;i < 6;i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;

}

 

 1.6 memcpy的模拟实现

memcpy的函数原型可知,其返回类型为无返回指针型,所以我们直接按照函数的原型来实现memcpy,这里我们将自己实现的memcpy命名为my_memcpy。

void* my_memcpy(void* dest, const void* src, size_t num) 
{
  while (num--) 
{
   *(char*)dest = *(char*)src;  //进行拷贝
    //移动指针
	dest = (char*)dest + 1;
	src = (char*)src + 1;
}
}

这里大家是否觉得自定义函数已经完成了呢?是否有疑问呢?

比如 dest = (char*)dest + 1; 为什莫不写成++的形式?

为什莫要强制转化成char*的形式?

如果有这样疑问的朋友,那你就忘记指针中的一个重要知识点了,void*类型的指针不能进行+-整数的运算。因此我们如果要一个字节一个字节的移动地址,那此时就需要将void*的指针强转成char*的指针之后再对其进行+-整数的操作。

那这个函数完成了嘛,实践出结果! 请看!!

尽管运行结果的正确的,但这个是因为我使用的是VS2020版本,强行的运行的结果,所以这个函数还是不完整的,根据提示我们短缺一个返回值(对于void类型的函数而言,它和void类型还是有区别的,void函数不需要返回值,但是void类型的函数是需要返回值的。) 

那返回值是什么呢?

memcpy函数介绍中我们不难发现,在memcpy中,函数的返回值是目标空间地址,所以在我们模拟实现的my_memcpy函数中同样可以将目标空间地址返回。为了确保返回的是目标空间的其实地址,我们可以在开始拷贝前,先将目标空间的起始地址记录下来,最后在拷贝结束后将起始地址返回给函数。修改后的如下

#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;  //进行拷贝
		//移动指针
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

 

1.7 my_memcpy与memcpy的区别

memcpy函数中,C语言规定它是无法对函数重叠部分进行拷贝的,在我们实现的my_memcpy中可以很好的印证这一点,当有空间重叠的情况存在时,my_memcpy在拷贝时输出的结果会出错,但是在我们在VS中测试memcpy对空间重叠的拷贝时,却能正常拷贝,这说明VS的功能强大。但是在其他的编译器中不一定可以实现了。

如下:

#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* dest, void* src, size_t n)
{
	assert(dest && src);
	void* ret = dest;
	while (n--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;

	}
	return ret;
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	//     目标:  1 2 1 2 3 4 5 8 9 10
	// 这里想将12345拷贝到34567
	my_memcpy(arr + 2, arr, 20);
	for (int i = 0;i < 10;i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

 

 

所以  memcpy只负责拷贝空间无重叠的情况。 

当空间出现重叠时的拷贝则需要调用我们接下来的函数——memmove 内存移动函数

2.memmove - 安全搬运工

2.1函数原型

void* memmove(void* dest, const void* src, size_t n);

与memcpy的区别是memmove处理的源内存块和目标存快内存可以重叠 。

memmove函数中我们不难发现,它与memcpy的功能是一样的,只不过相较于memcpymemmove可以实现重叠部分的拷贝。函数的使用我就不再过多赘述,与memcpy相同。接下来我们就来重点介绍一下如何实现memmove这个函数。

2.2 memmove的模拟实现

 当来源代码在前时,目标空间在后时,重叠空间为源空间的后侧与目标空间的前侧;

当目标空间在后,源空间在前时,重叠空间则为源空间的前侧与目标空间的后侧。 

 因此我们如果想要在拷贝时不会改变重叠空间的内容,那我们只能先处理重叠空间的内容,再来处理不重叠空间的内容,因此拷贝的方式就有两种情况:

void* my_memmove(void* dest, void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src) 
	{
		//当源空间在前,重叠空间在后,从后往前拷贝
		while (num--) 
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	else
	{
		//当源空间在后,重叠空间在前,从前往后拷贝
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	return ret;
}

这里字节大小是23,大家觉得有问题莫?

没有因为我们强制转化的时候是char*,单位是1个字节,已经高瞻远瞩到现在的情况了。

如果是int*的情况,则

 

总结:

本章节讲解了memcpy和memmove函数,也实现了他们的模拟实现。

  • 内存复制函数——memcpy
  • 内存移动函数——memmove

今天的内容到这里就全部结束啦,在下章为大家讲解剩下的三个函数。

如果大家喜欢博主的内容,可以点赞、收藏加评论支持一下博主,当然也可以将博主的内容转发给你身边需要的朋友。

最后感谢各位朋友的支持,咱们下章再见!!!