C语言内存函数


前言

C语言内存函数包括memcpy, memmove, memset, memcmp等,可以用于数据的复制、赋值、比较等;其操作以字节为单位,不关心数据的具体类型。

内存函数都包含在#include <string.h这个头文件下,在使用的时候要包含头文件。


我们先来看memcpy,和strcpy原理类似,

1. memcpy使用和模拟实现

1.1 memcpy的使用

在这里插入图片描述
如官网所示,strcpy将num字节的值从源指向的位置直接复制到目标指向的内存块。
使用时注意以下几点:

  1. 源指针和目标指针指向的对象的底层类型与此函数无关;结果是数据的二进制副本。
  2. 该函数不检查源代码中的任何终止空字符,它总是精确地复制num字节。
  3. 为了避免溢出,目标和源参数指向的数组大小应至少为num字节,并且不应重叠(对于重叠的内存块,memmove是一种更安全的方法。

如果src指向的数组长度小于num字节,如下图所示,对于未定义的内存数据,系统会拷贝所指向内存的脏数据。

在这里插入图片描述
值得一提的是,对于dest与src所指向内存重叠的话,推荐使用memmove函数进行值的拷贝,也就是说,memcpy是不保证在这种情况下结果的准确性的。然而VS这个满分选手,在dest与src重叠时也能够很好的实现值的拷贝,如下图所示。

在这里插入图片描述

1.2 memcpy的模拟实现

接下来我们思考如何进行memcpy函数的实现,当然我们只满足基本需求,暂时不考虑内存块重叠的情况。

#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* src, size_t num) {//参数设置与C语言官网一致
	assert(dest && src);//在使用指针之前要进行判空
	void* ret = dest;//将目的地指针进行存储,方便后续进行返回
	for (size_t i = 0; i < num; i++)//i的类型和num保持一致
	{
		*((char*)dest) = *((char*)src);
		dest = (char*)dest + 1;
		//注意:此处不能用dest++或++dest,因为dest是泛型指针,无法进行运算;
		//也不能用(char*)dest++或++(char*)dest,因为强制类型转换是暂时的,没有转换dest的类型,
		//只是将强转结果用于操作,在++的时候已经是下一步操作了,强转失效;
		//且C语言不支持(char*)dest++,但VS2022的.c文件支持++(char*)dest,.cpp文件两个都不支持
		src = (char*)src + 1;
		//上面三行代码也可优化为 *((char*)dest+i) = *((char*)src+i);
		//或者 *((char*)dest+num-i) = *((char*)src+num-i);但没必要
	}
	//也可使用while循环
	/*while (num--) //num-- 后置--,先使用,后--
	{
		*((char*)dest) = *((char*)src);
		dest = (char*)dest + 1;
		src = (char*)src + 1;
		//上面三行代码也可直接写成*((char*)dest+num) = *((char*)src+num);
	}*/
	return ret;
}
int main()
{
	int dest[5] = { 0 };
	int src[10] = { 1,3,9,18,36 };
	memcpy(dest, src, 17);
	for (int i = 0; i < 5; i++)
	printf("%d ", dest[i]);
	//下面是char类型数组的测试
	/*char arr1[] = { 'a','b','c','d' };
	char arr2[11] = "A";
	memcpy(arr2 + 1, arr1, 4);
	printf("%s\n", arr2);*/
	return 0;
}

VS2022 x64运行结果如下图所示

1 3 9 18 36

2. memmove使用和模拟实现

其实memmove与memcpy区别不大,就在于memmove对于目标地址和原地址重叠的情况也能很好的作用,而重叠的情况官网对memcpy不做要求。

2.1 memmove使用

在这里插入图片描述

  1. 将num字节的值从source指向的位置复制到destination指向的内存块。复制就像使用中间缓冲区一样进行,允许目标和源重叠。
  2. 源指针和目标指针指向的对象的底层类型与此函数无关;结果是数据的二进制副本。
  3. 该函数不检查源代码中的任何终止空字符,它总是精确地复制num字节。为避免溢出,目标和源参数指向的数组大小应至少为num字节。
  4. 返回指针destination。

2.2 memmove模拟实现

下面我们推演几种可能出现的情况,如下图,当目标指针(绿色)在源指针(橙色)之前,所要复制的源域与目标域部分重叠,如果从最后一个元素开始挨个往前赋值,也就是8变为11,7变为10,6变为9,我们期望的是原来(char*)src+3及之后的4个字节对应的元素8被赋给5的位置,但在复赋值之前,8已经变成11了,此时赋值,11被赋给5的位置,以及原本应该被赋给3、4的6、7也已经被改变了。但是,当从前往后赋值,不会出现这种情况,因此,当dest1在src之前,从前往后赋值;
同理,dest2在src后,应该从后往前对dest2进行赋值,;而当要复制的原区域与目标完全重叠的情况,此时无所谓前后顺序,无论是从前往后还是从后往前均可。
为了方便,我们算法思想为,当dest<src,从前往后赋值,当dest>=src,从后往前赋值;
在这里插入图片描述
代码如下图所示

#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memmove(void* dest, const void* src, size_t num) 
{
	assert(dest && src);
	void* ret = dest;
	if((char*)dest<(char*)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 arr1[] = { 1,2,3,4,5,6,7,8 };
	my_memcpy(arr1, arr1 + 2, 20);
	for(int i=0;i<8;i++)
		printf("%d ",arr1[i]);
}

VS2022x64运行结果如下图所示

3 4 5 6 7 6 7 8

3. memset函数的使用

在这里插入图片描述其实memset函数挺简单的,void * memset ( void * ptr, int value, size_t num );以字节为单位将传入指针及其之后共num个字节的内存都赋值为value,并且返回传入的指针;value接受的数值转化为char类型的数据进行赋值,如果value大于一个字节,只取一个字节。

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "abcdef";
	memset(arr1, 'xy', 5);
	printf("%s\n", arr1);
}

VS2022x64运行结果如下图所示

yyyyyf

一定要当心,memset是以字节为单位进行赋值的,如下代码将

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[10] = { 0 };
	memset(arr1, 1, 4);
	for(int i=0;i<10;i++)
		printf("%d\n", arr1[i]);
}

VS2022x64运行结果如下图所示

16843009 0 0 0 0 0 0 0 0 0 

我们打开调试的监视-内存窗口,可以看到arr1存储的数据是0x 01 01 01 01,也就是每个字节都被设为了1.

在这里插入图片描述


4. memcmp函数的使用

在这里插入图片描述
memcmp也很简单,以字节为单位比较两个传入指针ptr1与ptr2指向的数据,共比较num个字节;如果在比较过程中出现ptr指向值小于ptr2,返回大于零的数字,小于则返回小于0的数字,如果都相当,返回0.

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[10] = { 1,2 };
	int arr2[10] = { 1 };
	printf("%d\n", memcmp(arr1, arr2,5));
	printf("%d\n", memcmp(arr1, arr2, 4));
	printf("%d\n", memcmp(arr1+2, arr2, 4));
}

VS2022x64运行结果如下图所示

1
0
-1

总结

其实官网还有很多内存函数,大家有兴趣可以自行查阅。
内存函数注意,都是以字节为单位进行操作的,今天的内容比较简单,下次见啦!
在这里插入图片描述

  • 14
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值