C语言内存操作函数

Posted 蓝乐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言内存操作函数相关的知识,希望对你有一定的参考价值。

简述

本文将介绍几个关于内存操作常见的库函数,即memcpy,memmove,memcmp以及memset函数。

memcpy–内存拷贝函数

void * memcpy ( void * destination, const void * source, size_t num );

memcpy函数和之前所介绍的strcpy函数类似,只是这次拷贝的内容不是字符串,而是各种类型的内存,因此对于memcpy的num参数代表的内存的字节数,即:
1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
2.这个函数在遇到 ‘\\0’ 的时候并不会停下来。
那么对于memcpy函数的实现就变得很简单了,思路与strcpy一样,只不过要将两个指针强制类型转换为char* 型,代码如下:

void* my_memcpy(void* dest, void* src, size_t num)
{
	assert(dest && src);//防止对NULL解引用
	void* ret = dest;//返回dest指针最初所指的地址
	while (num--)//将src中num个字节的内存拷贝到dest中
	{
		*((char*)dest) = *((char*)src);
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

用整形数组来看看代码的效果如何:

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1, 20);//将arr1中20个字节即5个元素拷贝到arr2中
	int i = 0;
	for (i = 0; i < 5; i++)//将arr2中前5个元素打印
		printf("%d ", arr2[i]);
	return 0;
}

在这里插入图片描述
可以看出代码的实现是符合预期的,那么现在有一个问题,memcpy是否可以将同一个数组的内容进行拷贝,即对于source和destination有重叠的情况下:

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9 };
	my_memcpy(arr1 + 2, arr1, 20);//将arr1中20个字节即5个元素拷贝到arr1第三个元素开始的位置处
	int i = 0;
	for (i = 0; i < 9; i++)//将arr1数组打印
		printf("%d ", arr1[i]);
	return 0;
}

我们预期打印的内容是:121234589,但是运行后的结果如下:
在这里插入图片描述
这显然不符合我们的预期,那么原因是什么呢?我们根据代码的逻辑来画个图理解一下:
在这里插入图片描述
每次拷贝后的数据将覆盖掉原有数据,因此经过5个元素的拷贝后,数组arr1变成了:1 2 1 2 1 2 1 8 9.
但是当我们直接调用编译器库函数<memory.h>中的函数时,会发现结果时符合我们的预期的:
在这里插入图片描述
这并不是说我们所实现的memcpy函数不对,根据C语言标准,memcpy只需要实现没有重叠部分的内存拷贝即可,而编译器中的memcpy函数实际上是加强了功能那么这个功能是怎么实现的呢?下面我们将来介绍memmove函数。

memmove–处理内存有重叠部分的拷贝函数

void * memmove ( void * destination, const void * source, size_t num );

可以发现memmove函数的参数与memcpy的一样,其实二者的意义也是完全一样的,只不过memmove函数可以实现内存有重叠的情况,即:

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr + 2, arr, 20);//将arr数组中前5个元素拷贝到3到7个元素的位置上
	int i = 0;
	for (i = 0; i < 10; i++)//将arr数组打印
		printf("%d ", arr[i]);
	return 0;
}

在这里插入图片描述
那么这个函数要怎么实现呢?需要分集中情况讨论:
在这里插入图片描述
因此只需要在memcpy的基础上在添加一种从后往前的情况即可,代码实现:

void* my_memmove(void* dest, void* src, size_t num)
{
	assert(dest && src);//防止对NULL解引用
	void* ret = dest;//记录最初的dest所指的地址
	if (dest < src)
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		dest = (char*)dest + num - 1;
		src = (char*)src + num - 1;
		while (num--)
		{
			//*((char*)dest + num) = *((char*)src + num);
			*(char*)dest = *(char*)src;
			dest = (char*)dest - 1;
			src = (char*)src - 1;
		}
	}
	return ret;
}

介绍完memcpy和memmove两个函数后,接下来再介绍一下memcmp函数。

memcmp–内存比较函数

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

memcmp函数与strncmp函数类似,只不过memcmp函数可以比较任何类型的内容。用memcmp函数来比较,需要传入待比较的内存大小,单位是bit。
下面是memcmp比较两个字符串的例子:

int main()
{
	char str1[] = "never give up";
	char str2[] = "never get lost";
	int ret = memcmp(str1, str2, sizeof(str1));
	printf("%d\\n", ret);
	return 0;
}

在这里插入图片描述

memset–内存设置函数

void * memset ( void * ptr, int value, size_t num );

memset函数的各个参数含义:ptr–指针指向的待设置的内存块。
value–是待设置的变量,即填充到ptr所指的内存块中的变量,其类型是int。
num:所要设置的value的个数。
memset函数的作用是将num个value变量填充到ptr所指的内存块中。比如:

int main()
{
	char str[] = "never give up";
	memset(str, '0', 6);
	printf("%s\\n", str);
	return 0;
}

在这里插入图片描述
那么memset的实现也很简单了:

void* my_memset(void* ptr, int value, size_t num)
{
	assert(ptr != NULL);
	void* ret = ptr;//记录ptr最初所指的位置
	while (num--)//依次填充value到ptr所指的内存块中
	{
		*(char*)ptr = value;
		ptr = (char*)ptr + 1;
	}
	return ret;
}

以上是关于C语言内存操作函数的主要内容,如果未能解决你的问题,请参考以下文章

C语言 字符串操作函数及内存拷贝函数归总

C 语言文件操作 ( fflush 函数 | 刷新缓冲区示例代码 )

C语言内存操作函数

C 语言内存四区原理 ( 内存四区建立流程 )

C 语言文件操作 ( C 语言中的文件操作函数 | 磁盘与内存缓冲区 | 缓冲区工作机制 )

关于C语言程序代码加颜色的问题!