[C语言]内存操作函数详解(memcpy、memmove、memcmp、memset、memchr)

一. memcpy函数

1.1 函数原型及函数实现的功能

函数原型:void* memcpy(void* destination, const void* source, size_t num)

函数功能:将源空间source中前num个字节的内容拷贝到目标空间destination。

几点注意事项:

  • 函数的功能是拷贝前num个字节的内容,不是拷贝前num个元素。
  • 与字符串拷贝函数strcpy不同,memcpy函数在拷贝的过程中遇到''不会停止拷贝。
  • memcpy不止可以用于拷贝字符,还可以用于拷贝整型、浮点型数据等。

1.2 memcpy函数的使用方法演示

代码段1.1实现的功能是将整型数组arr2中的前5个元素拷贝到arr1中去。那么,在使用memcpy函数时,目标空间为arr1,源空间为arr2,总共需要拷贝5*sizeof(int)个字节的内容。程序运行的结果为1  2  3  4  5  0  0  0  0  0。

两点注意事项:

  1. memcpy函数的返回类型为void*,如果要使用一个参数来接收memcpy函数的返回值,则应进行强制类型转换,如int* pa = (int*)memcpy(arr1, arr2, 5 * sizeof(int))所示的格式。
  2. C语言标准不要求memcpy函数可以处理重叠的内存空间,虽然在VS2019编译环境下,memcpy函数可以处理重叠空间,但由于C标准不要求其具有处理重叠空间的能力,因此,应当避免使用memcpy处理重叠空间。如:int arr[6]={1,2,3,4,5,6}; memcpy(arr, arr+1,4),会出现空间重叠,不能这样处理。

memmove函数可以用来处理重叠的空间,本文第二章会进行讲解。

代码段1.1:

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

1.3 memcpy函数的模拟实现 

模拟实现代码:

void* my_memcpy(char* dest, const char* src, size_t num)
{
	char* ret = dest; //返回值
	while (num--)
	{
		*(char*)dest = *(char*)src;
		//这里需要dest指针和src指针均向后移动一个字节
		//但是由于void*不能进行加法运算
		//因此,要先将空类型转换为字符型指针,然后再+1
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

二. memmove函数

2.1 函数原型及函数实现的功能

函数原型:void *memmove( void *dest, const void *src, size_t num);

函数功能:将源空间src的前num个字节的内容移动到目标空间dest中去。与memcpy函数不同,memmove函数可以处理重叠的空间。

2.2 memmove函数的使用方法演示

  • 使用memmove函数处理不重叠的空间

代码段2.1使用memmove函数将整型数组arr2的前5个元素移动到arr1中,效果与使用memcpy一致。

代码段2.1:

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9 };
	int* pa = (int*)memmove(arr1, arr2, 5 * sizeof(int));
	for (int i = 0; i < 10; i++)
	{
		printf("%d  ", arr1[i]);
	}
	return 0;
}
  • 使用memmove函数处理重叠的内存空间

代码段2.2使用memmove函数将整形数组arr的前5个替换为第3-7个元素的内容,程序运行的结果为:2  3  4  5  6  5  6  7  8  9。

代码段2.2:

#include<stdio.h>
#include<string.h>
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int* pa = (int*)memmove(arr, arr + 2, 5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d  ", *(pa + i));
	}
	return 0;
}

2.3 memmove函数的模拟实现

由于memmove函数可以处理重叠的空间,因此,在模拟实现时要考虑两块空间的相互干涉问题。对此,分为三种情况进行讨论(假设要移动num个字节的内容):

  • 源空间的起始位置在目标空间的起始位置前方不超过num个字节内存的位置。此时,如果从前往后进行复制,则会发生干涉,应当从后往前进行内存内容的复制,图解如图2.1所示。
图2.1  源空间的起始位置在目标空间的起始位置前方不超过num个字节内存的位置时的图解

 

  • 源空间的起始位置在目标空间后方不超过num个字节内存的位置。此时,如果从后往前复制会发生干涉,应当从前往后复制,图解如图2.2所示。
图2.2 源空间的起始位置在目标空间后方不超过num个字节内存的位置时的图解

 

memmove函数模拟实现的代码: 

void* my_memmove(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	if (src <= dest)
	{
		//src的小于dest
		//从后往前复制
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	else
	{
		//src的大于dest
		//从前往后复制
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}	
	}
	return ret;
}

三.  memcmp函数

3.1 函数原型及实现的功能

函数原型:int memcmp(const void* ptr1, conat void* ptr2, size_t num);

函数功能:比较两块内存空间ptr1和ptr2前num个字节的空间的存储内容的大小。比较方式和返回值与strncmp基本相同。两者的不同之处是strncmp遇到会停止比较,而memcmp不会。

3.2 memcmp函数的使用方法演示

代码段3.1定义了两个字符型数组:arr1[] = "abcdef" 和 arr2[] = "abceef",分别通过strncmp和memcmp函数比较arr1和arr2前5个字符的大小,将返回值分别存入ret1和ret2中,程序运行结果显示ret1=0、ret2<0。因此,通过strncmp比较字符串前5个字符的结果为arr1=arr2,通过memcmp比较arr1和arr2前5个字节内存的内容的结果为arr1<arr2。可见,memcmp遇到并没有停止比较。

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abceef";
	int ret1 = strcmp(arr1, arr2, 5);
	printf("ret1 = %dn", ret1);
	int ret2 = memcmp(arr1, arr2, 5);
	printf("ret2 = %dn", ret2);
	return 0;
}

3.3 memcmp函数的模拟实现

模拟实现代码:

int my_memcmp(const void* str1, const void* str2, size_t num)
{
	while(num--) 
	{
		if ((*(char*)str1 == *(char*)str2))
		{
			str1 = (char*)str1 + 1;
			str2 = (char*)str2 + 1;
		}
		else
		{
			return *(char*)str1 - *(char*)str2;
		}
	}
	return 0;
}

四. memset函数

4.1 函数原型及函数的功能

函数原型:void *memset( void *dest, int c, size_t count );

函数功能:memset为内存设置函数,将目标空间dest前count个字节的内存空间的内容设置为c,因为字符型数据属于整型家族,因此在函数调用时,可以将字符型数据作为c传递给memset函数。函数的返回值为指向dest空间的起始位置的指针。

4.2 memset函数的使用方法演示

代码段4.1实现的功能是将字符数组arr1的前5个字符改为'x',程序运行解结果为:arr = xxxxx###

代码段4.1:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "########";
	char* ret = memset(arr, 'x', 5);  
	printf("arr = %sn", ret);  //arr = xxxxx###
	return 0;
}

4.3 memset函数的模拟实现

模拟实现代码:

void* my_memset(void* str, int c, size_t num)
{
	assert(str); //指针有效性检验
	void* ret = str;
	while (num--)
	{
		*(char*)str = c;
		str = (char*)str + 1;
	}
	return ret;
}

五. memchr函数

5.1 函数原型及函数的功能

函数原型:void *memchr( const void *buf, int c, size_t count );

函数功能:从buf指向的空间为起始位置,在后面count个字节的内存空间中寻找第一次出现c的位置,并返回指向第一次出现c的位置的指针,如果找了count个字节的内存还没有发现c,函数就返回空指针NULL。

5.2 memchr使用方法的演示

在代码段5.1中,以字符数组arr[]="abcdabcf"的首元素位置为起始点,在7个字节的范围内,分别查找字符b和字符f第一次出现的位置,程序运行的结果为:ret1 = bcdabcf、ret2 = (null)。

代码段5.1:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "abcdabcf";
	char* ret1 = (char*)memchr(arr, 'b', 7);
	printf("ret1 = %sn", ret1);
	char* ret2 = (char*)memchr(arr, 'f', 7);
	printf("ret2 = %sn", ret2);
	return 0;
}

5.3 memchr的模拟实现

模拟实现代码:

void* my_memchr(const void* str, int c, size_t count)
{
	assert(str);
	while (count--)
	{
		if (c == *(char*)str)
		{
			return str;
		}
		str = (char*)str + 1;
	}
	return NULL;
}

全文结束,感谢大家的阅读,敬请批评指正。