常用内存操作函数详解

目录

前言

内存区域划分与分配

内存操作函数

memcpy()函数

函数简介

memcpy()函数的模拟实现

memmove()函数

函数简介

memmove()函数的模拟实现

memcmp()函数

函数简介

memcmp()函数的模拟实现

memset()函数

函数简介

 memset()函数的模拟实现


前言

内存区域划分与分配

1.栈区(stack)-  程序运行时由编译器自动分配,存放函数的参数值,局部变量的值;

函数内的局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放;

2.堆区(heap)-  在内存开辟另一块存储区域;程序运行时用malloc() ,calloc(), realloc()或new申请任意多少的内存,程序员自己负责何时用free或delete释放内存;

3.静态区(static)  -  编译器编译时即分配内存,用于存放全局变量和静态变量;这块内存在程序整个运行期间都存在;

4.文字常量区  -  用于存放常量字符串,程序结束后由系统释放;

5. 程序代码区 - 存放函数体的二进制代码;

内存操作函数

通过访问地址的方式操作计算机内存的C语言内置函数;

memcpy()函数

函数简介

参数解读:

1.memcpy()函数从指针变量source指向的位置开始拷贝num个字节到指针变量destnation指向的内存空间,并且返回目标空间的起始地址;

//memcpy()函数头文件 #include <string.h>
# include <string.h>
int main()
{
	int arr1[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int arr2[5] = { 0 };
    //一个整型为4个字节,5个整型元素即为20个字节;
	memcpy(arr1, arr2, 20);
	int i = 0;
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

运行结果:

 2. memcpy()函数遇到 '\0'并不会停止拷贝;

int main()
{
	char arr1[8] = "abcdef";
	//arr2数组存放5个元素 x x \0 x \0
	char arr2[] = "xx\0x";
	memcpy(arr1, arr2, 4);
	int i = 0;
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%c", arr1[i]);
	}
	return 0;
}

运行结果:

3. 如果指针source指向的那块空间与指针destination指向的那块空间重叠,此时使用memcpy()函数,因为需要指定的字节数是未知的,可能拷贝的数据会覆盖掉原先的数据,导致结果超出预期;

对于同一块内存空间,当我们将 1, 2, 3, 4, 5拷贝到 3,4,5,6,7,我们期望的结果是1 2 1 2 3 4 5 8 9 10,但是我们将数字1挪动到数字3的位置,数字2挪动到数字4的位置,此时3的内容被修改,4的内容也被修改,此时输出结果为 1 2 1 2 1 2 1 8 9 10;

memcpy()函数的模拟实现

 实现思路:

1. 实现任意类型数据的拷贝,参数设置为void*用于接收任意类型数据,源头数据不需要修改内容,用const修饰,返回目标空间的起始地址,也用void*接收;

 2. 将src指向的数据拷贝到dest所指向的目标空间,内存的最小单元是一个字节,将任意类型的指针强制转换为char*,每次拷贝一个字节,从前到后拷贝,每次拷贝结束后,(char*)指针变量+1跳过一个字节,循环往复,拷贝的次数由指针跳过多少字节即字节数决定;

# include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest != NULL);
	assert(src != NULL);

	//记录起始位置地址
	void* ret = dest;
	while (num--)
	{
		*((char*)dest) = *((char*)src);
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

memmove()函数

函数简介

 参数解读:

1.memmove()函数从指针变量source指向的位置开始拷贝num个字节到指针变量destnation指向的内存空间,并且返回目标空间的起始地址;memmove()函数处理源内存块和目标内存块是可以重叠的

2.源内存块和目标内存块,使用memmove函数处理;

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

 运行结果:

memmove()函数的模拟实现

情形一:

对于同一块内存空间,当我们将 1, 2, 3, 4, 5拷贝到 3,4,5,6,7,从前向后拷贝,会覆盖掉原先的数据,那么从后向前拷贝呢?先把5挪到7的位置,4挪动到6的位置,3挪动到5的位置,2挪动到4的位置,1挪动到3的位置,此时结果为1 2 1 2 3 4 5 8 9 10,我们发现从后向前拷贝并不会覆盖掉原先的数据

情形二:

对于同一块内存空间,当我们将 3, 4, 5,6,7 拷贝到 1,2,3,4,5的位置,期望的输出结果是:3 4 5 6 7 6 7 8 9 10;当我们开始从后向前拷贝,将7挪动到5的位置,6的位置挪动到4的位置,此时原来数字5,4已经被数字7,6覆盖掉,输出结果:7 6 7 6 7 6 7 8 9 10;但是从前向后拷贝呢?数字3挪动到数字1的位置,数字4挪动到数字2的位置,数字5来到数字3的位置,数字6来到数字4的位置,数字7挪动到5的位置,输出结果:3 4 5 6 7 6 7 8 9 10;从前向后拷贝并不会覆盖原先的数据

总结: 数组在内存中是连续存放的,并且随着下标的变化地址由低到高变化,

         当src小于dest时,从后向前逐个字节的拷贝;

         当src大于dest时,从前向后逐个字节的拷贝;

void* my_memmove(void* dest, const void* src, size_t num)
{
	assert(dest != NULL);
	assert(src != NULL);

	void* ret = dest;
	//从前向后拷贝
	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;
}

memcmp()函数

函数简介

参数解读:

将ptr1所指向的内存块与ptr2所指向的内存块的前num个字节进行比较,按照字节进行比较

(每个字节均解释为unsigned char);

比较俩个内存块中第一对字节,如果彼此相等,继续比较下一对字节的大小,直到字节大小不同或者指定字节数比较完成;

返回值小于0,表示俩个内存块中不相等的第一对字节,ptr1中的值小于ptr2中的值;

返回值等于0,指定比较的字节数的内存块内容相同;

返回值大于0,表示俩个内存块中不相等的第一对字节,ptr1中的值大于ptr2中的值;

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcd";
	int ret = memcmp(arr1, arr2, 4);
	printf("%d ", ret);
	return 0;
}

运行结果:0

注:使用memcmp()函数比较字符串时在'\0'处不会停止比较

memcmp()函数的模拟实现

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	assert(ptr1 != NULL);
	assert(ptr2 != NULL);
	while (num--)
	{
		//强转为char*,进行逐字节比较
		if (*(char*)ptr1 != *(char*)ptr2)
		{
			return *(char*)ptr1 - *(char*)ptr2;
		}
		ptr1 = (char*)ptr1 + 1;
		ptr2 = (char*)ptr2 + 1;
	}
	return 0;
}

memset()函数

函数简介

 参数解读:

1.memset()函数是一个初始化函数,作用是将ptr所指向的内存块中前num个字节设置为指定的value值,逐字节设置value值,返回这块内存块的起始地址;

# include <string.h>
int main()
{
    //以16进制表示前俩个数字,从指定的起始位置开始将8个字节设置为0;
	int arr[10] = { 0x11111111, 0x22222222, 3, 4, 5, 6, 7, 8, 9, 10 };

	int*p=memset(arr, 0, 8);

	return 0;
}

内存窗口:

2.memset()函数是按照字节对内存块初始化,但是设置的值value是int类型4个字节,所以memset()函数只能取value值的后8位给输入范围的每个字节,即无论c多大只有后8位二进制是有效的;所以可以设置的value值的实际范围为0-255;

int main()
{   
    //-1的补码: 11111111 11111111 11111111 11111111

	int arr[10] = { 0x11111111, 0x22222222, 3, 4, 5, 6, 7, 8, 9, 10 };
	int*p=memset(arr, -1, 8);
	return 0;
}

内存窗口:

//1023的补码:  00000000 00000000 00000011 11111111
int main()
{
	int arr[10] = { 0x11111111, 0x22222222, 3, 4, 5, 6, 7, 8, 9, 10 };
	int*p=memset(arr, 1023, 8);
	return 0;
}

 内存窗口:

 memset()函数的模拟实现

void* my_memset(void* ptr, int value, size_t num)
{
	assert(ptr != NULL);
	void* ret = ptr;
	while (num--)
	{
		*((char*)ptr) = value;
		ptr=(char*)ptr + 1;
	}
	return ret;
}

  • 45
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小呆瓜历险记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值