C语言内存函数详解

                                                    

内存函数就是对内存中的数据进行操作的函数,常见的有memcpy,memmove,memcmp,memset。这些函数使用时需包含<string.h>库文件。

目录

 一.memcpy函数

1.模拟实现memcpy函数 

二.memmove函数

三.memcmp函数

1.模拟实现memcmp函数

四.memset函数

1.模拟实现memset函数


 一.memcpy函数

 memcpy意思就是把内存中的n个字节的数据复制到另一块内存中,下面是cplusplus.com网站该函数的定义。

第一个参数是 void* destination,void* 说明可以接收任意类型的指针,第二个参数是 const void* source 有const说明该指针指向的内存数据不可修改,第三个参数是 size_t num,是一个无符号整形,返回值是void*,没有指定类型的指针。

意思是把source所指向的内存往后num个字节的数据都复制到destination所指向的内存中,返回的是刚开始时destination的指针。

#include<stdio.h>
#include<string.h>//内存函数要引用这个库函数
int main()
{
	char str1[10] = {"abcdef"};
	char str2[10];
	memcpy(str2, str1, 7);//把str1里面abcdef拷贝到str2里,有6个元素+'\0',因为char占一个字节所以是7个字节
	int a = 10;
	int b = 0;
	memcpy(&b, &a, 4);//int类型是4个字节
	printf("str2:%s\nb=%d", str2, b);
	return 0;
}

1.模拟实现memcpy函数 

知道了怎么使用之后我们就来模拟实现一个memcpy函数。

 思路:把source和destination的地址进行强制类型转换成char*这样每次解引用以及+1的时候都是操作一个字节,把num个字节的数据拷贝完之后就把最开始的destination的地址返回就好了。

#include<stdio.h>
void* my_memcpy(void* dest, void* src, size_t num)
{
	void* ret = dest;//保存开始时dest的地址
	while (num--)//num为0跳出循环
	{
		*((char*)dest)++ = *((char*)src)++;//强制转换成char*一个字节一个字节拷贝
	}
	return ret;//返回开始时dest的地址
}
int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	int i = 0;
	my_memcpy(arr2,arr1,24);//把arr1中前6个元素拷贝到arr2中,int是4字节,6个元素就是24字节
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

可以看到确实是把arr1前6个元素(24个字节)拷贝到了arr2中。

那么如果我们想把arr1前5个数字1,2,3,4,5拷贝到3,4,5,6,7的位置去,这样还能不能成功呢?

如果成功的话输出的应该是1,2,1,2,3,4,5,8,9,10。

int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	int i = 0;
	my_memcpy(arr1+2,arr1,20);//把1,2,3,4,5拷贝到3,4,5,6,7的位置
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

结果却和我们想象的1,2,1,2,3,4,5,8,9,10不一样。这是因为我们把1和2拷贝到3和4的位置之后,下次再想把3拷贝到5的位置时3其实已经被1覆盖了,所以会导致出现循环1,2的情况。

注意下面的图例中每一个数字代表着4个字节(int类型)。

 

像这种dest和src有重叠的部分应该用memmove函数处理(memmove也是把一块内存数据移动到另一块内存去),memcpy不适用这种情况,但是VS编译器的memcpy可以完成这种dest和src重叠的内存拷贝。

我们接下来完善这个memcpy函数。

 思路:对于dest和src有重叠的时候我们可以分为两种情况,一种是dest的地址大于src的情况,另一种是dest的地址小于src的情况。

第一种情况是dest地址大于src地址。就是上面那种情况,我们可以从后往前拷贝,先把5拷贝到7那里,再把4拷贝到6那里,随后再把3拷贝到5那里,就可以把1,2,3,4,5拷贝到3,4,5,6,7的位置去了。

第二种情况就是dest的地址小于src的地址,就可以按正常情况从前往后拷贝。

 下面是代码实现:

#include<stdio.h>
void* my_memcpy(void* dest, void* src, size_t num)
{
	void* ret = dest;
	if(dest<src)//判断dest和src的地址大小
	{
		while (num--)//dest地址小于src地址就从前往后拷贝
		{
			*((char*)dest)++ = *((char*)src)++;
		}
	}
	else
	{
		while (num--)//dest地址大于src地址就从后向前拷贝
		{
			*((char*)dest+num) = *((char*)src+num);//从最后一个字节开始,一个字节一个字节的向前拷贝
		}
	}
	return ret;
}
int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	int i = 0;
	my_memcpy(arr1+2,arr1,20);//把1,2,3,4,5拷贝到3,4,5,6,7的位置
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

 

二.memmove函数

下面是cplusplus.com网站给出的函数定义。 

可以看到和memcpy是完全一样。

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的,如果源空间和目标空间出现重叠,就得使用memmove函数处理。

而我们上面已经解决了源内存块和目标内存块重叠的情况了,所以就不多说了。一般拷贝源内存块和目标内存块有重叠的时候就用memmove,没有重叠就用memcpy。

三.memcmp函数

memcmp是内存比较函数,下面是定义。

 比较两个指针所指向内存块的num个字节的大小。如果相等返回0,如果ptr1小于ptr2返回小于0的数字,反之返回大于0的数字。

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[5] = { 1,2,3,4,5 };//内存中的数据 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00(20个字节)
	int arr2[5] = { 1,2,3,4,6 };//内存中的数据 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 06 00 00 00(20个字节)
	int ret = memcmp(arr1, arr2, 16);//比较两个指针指向内存块的前16个字节
	printf("%d", ret);
	return 0;
}

 

 前16个字节都一样,所以返回0。

这里涉及到了小端大端的存储,就是数据在内存中存储的字节序,如果不懂可以看我关于大小端的博客:数据在计算机中的存储

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[5] = { 1,2,3,4,5 };//内存中的数据 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00(20个字节)
	int arr2[5] = { 1,2,3,4,6 };//内存中的数据 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 06 00 00 00(20个字节)
	int ret = memcmp(arr1, arr2, 17);//比较两个指针指向内存块的前17个字节
	printf("%d", ret);
	return 0;
}

 第十七个字节arr1是小于arr2的,所以返回小于0的数字。

1.模拟实现memcmp函数

#include<stdio.h>
int my_memcmp(void* ptr1, void* ptr2, size_t num)
{
	while (num--)
	{
		if (*(char*)ptr1 != *(char*)ptr2)//比较每一个字节大小是否相同·
			return *(char*)ptr1 - *(char*)ptr2;
		ptr1 = (char*)ptr1 + 1;//指针往后移动一个字节
		ptr2 = (char*)ptr2 + 1;//指针往后移动一个字节
	}
	return 0;//如果都相同就返回0
}
int main()
{
	int arr1[5] = { 1,2,3,4,7 };
	int arr2[5] = { 1,2,3,4,6 };
	int ret = my_memcmp(arr1, arr2, 17);
	printf("%d", ret);
	return 0;
}

 

四.memset函数

下面是定义。

 这个函数的作用就是把ptr所指向的内存块往后num个字节改成value的值,返回开始的ptr指针。

#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[5] = { 0 };
	memset(arr1, 1, 12);//把arr1所指向的内存往后12个字节都改成1
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

 

这里我们可以观察内存,看为什么把12个字节改成1就会出现这三个数字。

 这是arr1在内存中的数据,可以看到memset函数确实把前十二个字节改成了1,但这是16进制的数字,我们还需转换成10进制。每四个字节就是一个int类型,每一行代表着一个数组的数字,所以我们只用看4个字节就好,16进制01010101转换成2进制就是00000001 00000001 00000001 00000001,用计算机计算一下转换成10进制就是16843009了。

 

1.模拟实现memset函数

#include<stdio.h>
void* my_memset(void* ptr, int value, size_t num)
{
	void* ret = ptr;
	while (num--)
	{
		*((char*)ptr)++ = value;//把每个字节都改成value的值
	}
	return ptr;
}
int main()
{
	int arr1[5] = { 0 };
	my_memset(arr1, 1, 12);//把arr1所指向的内存往后12个字节都改成1
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

 


 以上就是关于常见内存函数的介绍和模拟了,使用的时候也要记得引用库函数<string.h>如有错误还请指正。

喜欢就请点个赞,感谢你的观看。

                                                 

 

  • 15
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值