C 语言标准库的内存操作函数定义在 <string.h> 头文件中,直接按字节操作内存块,不依赖字符串结束符\0,可处理任意类型数据。以下是 4 个核心函数:

1. memcpy — 内存拷贝

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

功能:从src内存地址拷贝n个字节到dest地址

返回值:目标内存首地址dest

注意:不处理内存重叠,若destsrc区域重叠,结果未定义;重叠场景必须用memmove

以下函数中,将arr中的前20个字节的元素拷贝到arr2中去,也就是只拷贝5个整形1,2,3,4,5

int main()

{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

(1)memcpy的模拟实现

void my_memcpy(void* dest, const void* src, size_t num)
{
	char* ret = dest;
	assert(dest && src);
	while(num--)
	{
		*(char*)dest = *(char*) src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}
int main()

{
	int src[] = { 1,2,3,4,5,6,7,8,9,10 };
	int dest[10] = { 0 };
	my_memcpy(dest, src, 40);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", dest[i]);
	}
	return 0;
}
 

2. memmove — 可重叠内存拷贝

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

功能:与memcpy完全一致,支持内存重叠区域的安全拷贝

原理:内部判断重叠方向,自动选择从前往后或从后往前拷贝,保证数据正确

场景:数组内元素移位、同一块内存的前后拷贝

以下代码中,属于重叠内存的拷贝,将arr数组中的前20个字节,也就是前5个整形拷贝到arr+2往后的地址中去,arr+2指向元素3的位置,拷贝的后的arr数组就变为1,2,1,2,3,4,5,8,9,10

int main()

{
	int arr[10] = { 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]);
	}
	return 0;
}

(1)memmove的模拟实现

在模拟实现memmove函数时我们需要注意,因为拷贝的是在同一个内存空间里拷贝的,所以我们得分情况讨论

第一种,当dest指针在src后面时:


我们将src中的元素赋值给dest的位置时,可以看出,先把4赋给6,然后5赋给7,但是这时注意,这里的6和7已经变成了4和5,所以继续往后赋值,原本是将6赋值给8的,但是现在6的位置已经变成了4了,所以只能将4赋值给8,然后再啊5赋给9,最后再把4赋值给10

这样就达不到我们想要的效果,既然正这赋值不行,那我们可以反着赋值,先将8赋给10,然后将7赋给9,依次赋值,这样就避免了上述重复赋值情况

第二种,dest在src前面时:

这时我们就可以正着赋值了,先将5赋给2,然后依次类推,6赋给3……

第三种,dest在src后面且不重合

这时候,无论我们正着赋值,还是倒着赋值都可以

我们将上述三种情况总结,当dest在src前面时,我们选择正赋值,当dest在src后面时,我们选择倒赋值

正赋值和上面模拟实现memcpy函数一样

while(num--)
	{
		*(char*)dest = *(char*) src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

主要是怎么实现倒赋值呢,我们首先要让指针指向dest和src的最后一位,怎么做呢,我们知道数组名也表示首元素地址,那么总数组名+数组长度-1就指向了数组最后一位元素,我们就可以写如下代码

*((char*)dest + num) = *((char*)src + num);

来分别找到dest和src的最后一位,然后依次赋值即可,完整代码实现如下

void my_memmove(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);
	if (dest < src)
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	
	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]);
	}
	return 0;
}

3. memset — 内存填充

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

功能:将ptr指向的前n个字节全部设置为value(按字节赋值)

注意:按字节赋值,只能准确初始化0-1(全 0 / 全 1 字节模式),不能直接给整型数组赋值其他数值

int main()

{
	char s[] = "hello world";
	memset(s, 'i', 5);
	printf("%s ", s);//s=iiiii world

	return 0;
}

为什么不能给整形数组赋值呢,观察以下代码

int main()

{
	int arr[] = { 1,2,3,4,5 };
	memset(arr, 2, 4);
	for (int i = 0; i < 5; i++)
		printf("%d ", arr[i]);
	return 0;
}

我们将2赋给arr数组的第一个元素,刚好是4个字节,但是我们将其打印出来观察结果

发现第一个元素不是2,为什么呢?这是因为memset函数是一个一个字节赋值的,下arr数组的内存

可以发现,它的确将第一个元素的4个字节全部赋值成了2,但是数字2是由00 00 00 02构成的,而02 02 02 02合在一起并不是数字2

4. memcmp — 内存比较

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

功能:按字节比较两个内存块的前n个字节

返回值:

< 0:ptr1 小于 ptr2

= 0:两块内存完全相等

> 0:ptr1 大于 ptr2

注意:遇到\0不会停止比较,这是与strcmp的核心区别

int main()

{
	char s1[] = "abcde";
	char s2[] = "abcdf";
	int ret=memcmp(s1, s2, 5);
	if (ret < 0)
	{
		printf("s1<s2");
	}
	else if (ret == 0)
	{
		printf("s1==s2");
	}
	else
	{
		printf("s1>s2");
	}
	return 0;
}

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐