C语言中内存函数


memcpy

前面介绍了专门拷贝字符串的函数strcpy,但是strcpy只能拷贝字符串
如果想拷贝其他类型的内存空间,就需要用到memcpy函数

void * memcpy ( void * destination, const void * source, size_t num );
  • memcpystring.h文件中,使用时应包含头文件。
  • 这里的num是字节数。
  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
#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);//将arr1中的前20个字节拷贝到arr2空间中
	
}

memcpy(arr2, arr1, 20);,是将arr1中的前20个字节拷贝到arr2空间中,20个字节也就是前5个int类型的元素
运行程序,进入调试监控窗口可以看到,已经成功将arr1中前20个字节的内容拷贝到了arr2
在这里插入图片描述
如果想拷贝3、4、5、6、7,可以变源头,memcpy(arr2,arr1+2,20)即可


模拟实现memcpy

因为我们不知道要拷贝的类型是什么,所以就需要用void*接收目标空间地址和源空间地址,所以函数定义为:void* my_memcpy(void* des, void* src, size_t num)

接下来,因为不知道拷贝的是什么类型,所以在拷贝的时候,需要将void*类型的指针强制转换成char*类型,这样可以一个字节一个字节地进行拷贝

*(char*)des = *(char*)src;

并且值得注意的是,在挪动指针的时候,平常我们都习惯使用后置++,但是因为这里的指针为void*类型,不能直接后置++
可以前置++如:

++(char*)des;
++(char*)src;

如果想使用后置++,也可以这样:

((char*)des)++;
((char*)src)++;

或者也可以这样:

des = (char*)des + 1;
src = (char*)src + 1;

所以根据之前的模拟strcpy以及上述的几点,可以写出模拟memcpy函数:

//memcpy
#include <stdio.h>
#include <assert.h>
#include<string.h>
void* my_memcpy(void* des, void* src, size_t num)
{
    
    
	assert(des && src);
	void* ret = des;
	while (num > 0)
	{
    
    
		*(char*)des = *(char*)src;
		des = (char*)des + 1;
		src = (char*)src + 1;
		/*((char*)des)++;
		((char*)src)++;*/
		num--;
	}
	return ret;
}

memmove

前面讲了memcpy函数,但是如果在同一块内存空间上使用mencopy函数也许会有问题:
如果有一个数组

int arr[] = {
     
      1,2,3,4,5,6,7,8,9,10 };

如果想4、5、6、7拷贝到6、7、8、9的位置上时就会出错误
在这里插入图片描述
在将 4 和 5 拷贝到原先 6 和 7 的位置上后,接着拷贝,本来是应该将 6 拷贝到 8 的位置上,结果本应是6的位置却在之前的拷贝被 4 占了,就出了错误
在这里插入图片描述

所以为了解决这问题,就有了memmove函数,它即包含了之前memcpy的功能,也可以叠加拷贝

void * memmove ( void * destination, const void * source, size_t num );
  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。
#include <stdio.h>
#include<string.h>
int main()
{
    
    
	int arr1[] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	memmove(arr1+2, arr1, 20);
}

模拟实现memmove

在模拟实现前,我们先西靠一下如果解决重叠的拷贝问题

dessrc前面时,我们发现,可以从前往后进行拷贝,不会出现问题

如图:
在这里插入图片描述

  • 把 4 拷贝到 1 的位置上
  • 把 5 拷贝到 2 的位置上
  • 把 6 拷贝到 3 的位置上
  • 把 7 拷贝到 4 的位置上
    不会出现被覆盖数据的问题

dessrc后面时,如果还从前往后拷贝就会出现被覆盖的问题,所以需要从后往前拷贝

在这里插入图片描述

  • 把 7 拷贝到 9 的位置上
  • 把 6 拷贝到 8 的位置上
  • 把 5 拷贝到 原先 7 的位置上
  • 把 4 拷贝到 原先 6 的位置上
    只有这么倒着拷贝,就不会有问题

模拟实现memmove

void* my_memmove(void* des, void* src, size_t num)
{
    
    
	assert(des && src);
	void* ret = des;
	if (des < src)
	{
    
    
		while (num > 0)
		{
    
    
			*(char*)des = *(char*)src;
			des = (char*)des + 1;
			src = (char*)src + 1;
			num--;
		}
	}
	else
	{
    
    
		char* src_pos = (char*)src + num-1;
		char* des_pos = (char*)des + num-1;
		while (num>0)
		{
    
    
			*des_pos = *src_pos;
			src_pos = src_pos-1;
			des_pos = des_pos-1;
			num--;
			
		}
	return ret;
}

这里值得注意的一点时,当倒着拷贝时,首先要把src空间中最后一个字节内容拷贝到des空间的最后一个字节中
num是函数传进来一个表示字节的参数,src空间中最后一个字节内容不是:(char*)src + num,而是(char*)src + num-1

原因如下图:
在这里插入图片描述

可以对上面的代码进行简化:

void* my_memmove(void* des, void* src, size_t num)
{
    
    
	assert(des && src);
	void* ret = des;
	if (des < src)
	{
    
    
		while (num > 0)
		{
    
    
			*(char*)des = *(char*)src;
			des = (char*)des + 1;
			src = (char*)src + 1;
			num--;
		}
	}
	else
	{
    
    
		while (num--)
		{
    
    
			*((char*)des + num) = *((char*)src + num);
		}
	}
	return ret;
}

这里为了保证让最开始拷贝的地址为src+num-1,在while条件中,就已经num--一次了,就保证了从最后一个字节开始拷贝


memcmp

int memcmp ( const void * ptr1,const void * ptr2,size_t num );
  • 比较从ptr1和ptr2指针开始的num个字节
  • 返回值情况同strcmp函数

模拟实现memcmp

int my_memcmp(void* ptr1, void* ptr2, size_t num)
{
    
    
	assert(ptr1 && ptr2);
	while (num > 0)
	{
    
    
		while (*(char*)ptr1 == *(char*)ptr2)
		{
    
    
			if (num == 1)
			{
    
    
				return 0;
			}
			ptr1 = (char*)ptr1+1;
			ptr2 = (char*)ptr2+1;
			num--;
		}
		if (*(char*)ptr1 < *(char*)ptr2)
		{
    
    
			return -1;
		}
		if (*(char*)ptr1 > *(char*)ptr2)
		{
    
    
			return 1;
		}
	}
}

memset

void * memset ( void * ptr, int value, size_t num );
  • memset为内存设置函数
  • 以字节为单位来设置内存中的数据
#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char arr[] = "hello world";
	memset(arr, 'x', 5);
	printf("%s", arr);
}

arr的前5个字节内容改为'x',输出结果:
在这里插入图片描述

有一个int类型的数组int arr[10] = {0},如果使用memset(arr, 1, 40);会有什么结果呢?

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	int arr[10] = {
    
    0};
	memset(arr, 1, 40);
}

进入监视就可以看到,数组内的数据有0变为了一个很大的数
在这里插入图片描述

这是因为memset函数将这个数组40个字节全部复制为1,而不是将每个整形赋值为1,二进制为00000001000000010000000100000001的数字就是16843009

这里可以看出,memset函数对于字节数为1的字符类型很适用,对于2、4、8字节的类型不太适用

这里有一种对于2、4、8字节适用的情况,就是把他们的各个字节都变为0,也就是把变量赋值为0

int arr[10] = {
     
      0 };
memset(arr, 0, 40);

猜你喜欢

转载自blog.csdn.net/weixin_64116522/article/details/128765551
今日推荐