IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 内存函数分析 -> 正文阅读

[C++知识库]内存函数分析

前面我们介绍了strcpy、strcat、strstr等字符串函数的操作及模拟实现,我们可以方便地通过使用这些函数操作字符串完成拷贝,拼接、查找等一系列操作。但是这些函数还是有一定地局限性,

就是这些函数只能操作字符串!而我们有的应用场景下,我们需要对两个非字符串地集合类型拷贝操作,这时候这些字符串函数就不能够满足我们的需求。设计者同样考虑到了这种情况,因此还提供了一系列新的内存函数来处理这些情景!接下来,我们就来认识一下memcpy、memmovememset等内存函数,看看如何使用它们进行更好的程序设计!

一.memset:

我们先来看memset的函数原型:

void *memset( void *dest, int c, size_t count );

dest:目标空间的起始地址

c:指定的初始化元素

count:初始化空间的大小,以字节为单位

不难看出,memset的功能是将从某个地址开始处的大小为count字节的内容初始化为c,并将初始化后的目标空间的首地址返回来!接下来我们看一看memset的实际应用:

#include<stdio.h>
#include<string.h>
//使用memset改变数组的元素
int main()
{
	int arr[10];
	memset(arr, 0, 40);
	return 0;
}

我们知道,局部数组如果不初始化,数组的内容是随机值,但我们使用了memset对数组进行了初始化以后,我们打开内存窗口观察arr的内容:

?数组的内容被我们全部都初始化成了0,达到了我们想要的效果!

注意!memset的初始化是按照字节作为单位进行的!假设你想把arr数组的元素初始化成全1,那么不能简单地直接使用memset,接下来我给你详细地说明:

假设arr数组被初始化成全0,接下来你写了这么一段代码:

memset(arr,1,10*sizeof(int))

可能你地本意是将所有元素初始化成1,但实际上并不是这样,真实情况如下:

00000000?00000000?00000000?00000000----->这是其中一个元素的32个2进制位

00000001 00000001 00000001 00000001----->这是按照上面的代码初始化以后的结果

可以明显发现,这个数字显然不是1,因为memset是按照一个字节一个字节地初始化,所以要想使用memset初始化数组元素位全1,应该要这样写

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
//使用memset改变数组的元素
int main()
{
	int arr[10]={0};
	int* ret = arr;//使用指针记录初始位置
	for (ret = arr; ret != arr + 10; ret++)
	{
		memset(ret, 1, 1);//每次只设置一个字节改变
	}
	return 0;
}

你会发现这样做确实会非常麻烦,用循环的方式会更方便!确实如此,所以更多情况下memset初始化元素通常是全0或者是字符元素。

二.memcpy:

前面我们讲了strcpy拷贝函数,strcpy能够处理字符串的拷贝工作,但是假设我们要处理两个整型数组的拷贝,那我们应该怎么处理呢?对此,C语言提供了memcpy函数解决这个问题!

我么先来看memcpy函数的函数原型:

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

dest:目标空间的起始地址

src:源空间的起始地址

count:拷贝的内容的长度,以字节为单位长度

接下来,我们通过一个案例来了解一下memcpy函数:

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

运行结果如下:

?可见memcpy函数确实完成了拷贝数组的工作,接下来我们不妨上手模拟实现一下这个函数

//memcpy的模拟
void* my_memcpy(void* dest,const void* src,size_t count)
{  
	void* ret = dest;//保存起始位置
	assert(dest && src);//防止空指针的解引用
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = ((char*)dest)++;
		src = ((char*)src)++;
	}
	return ret;
}
int main()
{
	int arr1[5] = { 0 };
	int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr1, arr2, 5 * sizeof(int));
	printf("my_memcpy的结果:\n");
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

接下来对代码进行解析:

void* my_memcpy(void* dest,const void* src,size_t count)

从函数声明来看,完全模仿库函数的函数声明,因为设计者并不知道未来memcpy将要拷贝的元素的类型是什么,所以使用void*类型的指针来接受任意类型的数据

接下来,我们在来看内部实现:

在内部实现的代码,我们将dest和src强制转换成了char*,这么做的理由有两个:

1.void*的指针不允许直接解引用,所以我们要想拿到dest和src里的内容就要对它们强制类型转换.

2.因为指针的类型决定的是解引用一次能访问几个字节的内容以及自增一次会跳过多少个字节,而char*刚好是访问一次是一个字节,所以我们只需要知道要交换的字节内容的宽度,每次一个字节一个字节的交换即可。

程序运行结果如下:

?我们的memcpy也很好地完成了拷贝的任务,这里还有几个memcpy的注意点:

1.必须保证目标空间足够大,否则程序会因为非法访问而崩溃!

2.必须帮正目标空间的内容可以修改,否则程序同样会崩溃!

三. memmove

前面我们介绍了memcpy函数,对于大多数情况下,memcpy函数已经够用了,但还是有一种情况,那就是目标空间和源空间存在内存重叠的情况,这种情况下如果是我们写的memcpy函数就会出现部分数据被覆盖的情况,所以设计者考虑到这种情况以后又设计出了memmove函数处理内存重叠的情况。、

我们先来看memmove的函数原型:

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

从函数原型上来看,它和memcpy并没有差别,那么我们通过一个案例来使用memmove

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>
//memmove的使用
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
	//结果应该是3 4 5 6 7 6 7 8 9 10
	memmove(arr, arr + 2, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

运行结果如下:

?发现memmove确实很好地处理了内存重叠的问题,接下来我们实现一下memmove函数

void* my_memmove(void* dest, const void* src, size_t count)
{
	void* ret = dest;
	assert(dest && src);
	if (dest < src)
	{
		while (count--)
		{
			*(char*)dest = *(char*)src;
			dest = ((char*)dest)++;
			src = ((char*)src)++;
		}
	}
	else
	{
		while (count--)
		{
			*(((char*)dest) + count) = *(((char*)src) + count);
		}
	}
	return ret;
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
	//结果应该是3 4 5 6 7 6 7 8 9 10
	//memmove(arr, arr + 2, 20);
	my_memmove(arr, arr + 2, 5 * sizeof(int));
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

对于这段代码,结合下面的图片会更好理解

?注意:这里的从后向前和从前向后都指的是字节为单位,也就是从第一个字节和最后一个字节开始拷贝,所以这里的dest和src都被强转成了char*进行内存的访问!

程序运行结果如下:

?将arr+2和arr位置对调,程序的结果如下:

?由此,我们可以看出我们的memmove函数也达到了和库函数一样类似的功能

以上就是部分内存函数的内容介绍,祝大家新春愉快!

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-29 22:54:07  更:2022-01-29 22:54:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:17:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码