首页 > 系统相关 >内存操作函数详解

内存操作函数详解

时间:2023-03-11 17:31:39浏览次数:40  
标签:src 函数 dest void char 详解 内存 复制 memcpy

这是一篇介绍内存操作函数的博客,包含memcpy、memmove、memcmp、memset的具体介绍及模拟实现,也与相似的字符串操作函数进行了比较。

一、MSDN中的注解

1、memcpy

image.png在缓冲区之间拷贝字符; 也就是从src中拷贝count个字节的数据到dest中。

2、memmove

image.png将一个缓冲区移动到另一个缓冲区; 也是从src中拷贝count个字节的数据到dest中,既然两个函数的作用相同,那为什么又增加了memmove呢? 是因为 memmove可以实现重叠区域的拷贝

举例来说:

如下数组image.pngsrc指向3的地址,dest指向5的地址,此时若要将src中的5个字符复制到dest中,使用memcpy和memmove便会有明显的区别;

  • 使用memcpy时: 由于字符复制是从前往后进行的,复制3时,将5改成了3,复制4时,将6改成了4,如此便会造成原本的5和6丢失,最终复制的结果便是:image.png
  • 使用memmove时 而memmove会根据src与dest的地址大小进行判断,从而选择不同的复制方法,在此例中,memmove便会使用从后往前复制的方法,最终可以正确完成复制:image.png

3、memcmp

image.png比较两个缓冲区中的字符; 与strcmp相似,返回值为int,buf1>buf2返回正值,buf1<buf2返回负值,相等返回0;增加的count则是需要比较的字节数目。

4、memset

image.png将缓冲区设置为一个特定的字符;其实就是初始化,特殊的是:memset会将从dest开始后count个字节全部设置为c值。

二、模拟实现

1、memcpy

  • 将void类型的至今强制转换为char*指针,进行复制操作;
  • 随后以char*确定步长为1,逐字节复制;
  • 用size_t的无符号整型n接收需要复制的长度,自减完成复制操作;
  • assert断言,确保dest与src不为空指针;

如下:

void* my_memcpy(void* dest, const void* src, size_t n)
{
	assert(dest && src);
	void* ret = dest;
	//从前往后复制
	while (n--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

2、memmove

增加对dest与src的地址大小判断,由于计算机保存数据从低地址开始,所以:

  • src < dest时,从前往后复制;
  • 反之,从后往前复制;
  • 在进行从后往前的复制时,只需将void的指针强制转换为char,随后加上需要复制的字节数n,便可到达需要复制的内存末尾;

如此便可实现重叠字符串的复制效果:

void* my_memmove(void* dest, const void* src, size_t n)
{
	//断言确保不为空指针
	assert(dest && src);
	void* ret = dest;
	//从前往后复制
	if (dest < src)
	{
		while (n--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	//从后往前复制
	else
	{
		while (n--)
		{
			*((char*)dest + n) = *((char*)src + n);
		}
	}
	return ret;
}

3、memcpy

memcpy的模拟实现与strcpy的有很大相似之处,不同点便是:

  • memcmp利用void*类型接收,因此在逐字节比较时需要强制类型转换,同样在往后走时,也需要强制类型转换为char*,从而确定步长;
  • 二者的循环结束调节不同,strcpy以‘\0’作为判别标志,而memcpy以确定的大小传参,比较完成便结束,若不同则返回结果,以此带来的,memcpy也更为安全;
int my_memcmp(const void* e1, const void* e2, size_t n)
{
	assert(e1 && e2);
	while (n--)
	{
		if (*(char*)e1 != *(char*)e2)
		{
			return *(char*)e1 - *(char*)e2;
		}
		e1 = (char*)e1 + 1;
		e2 = (char*)e2 + 1;
	}
	return 0;
}

4、memset

此函数相对简单,只需将void*指针强制类型转换后赋值即可;

void* my_memset(void* dest, int c, size_t n)
{
	assert(dest);
	void* ret = dest;
	while (n--)
	{
		*(char*)dest = c;
		dest = (char*)dest + 1;
	}
	return ret;
}

三、易错点

  • 内存操作函数是以字节为单位进行函数运算,所以,当一段代码在大端和小端模式上分别运行时,结果可能不同。

标签:src,函数,dest,void,char,详解,内存,复制,memcpy
From: https://blog.51cto.com/u_15423682/6114677

相关文章

  • Java内存模型:Java解决可见性和有序性问题的方案
    Java内存模型并发场景下,可见性/原子性/有序性是并发编程Bug源头,而Java内存模型解决了可见性和有序性问题。Java内存模型定义可见性问题原因是缓存,有序性问题原因是编译......
  • 【Python】main函数 if name=='main' 详解
    引言Python代码print('hellowword')代码执行顺序我们可以看到Python仅仅用了一行代码即可完成其他编程语言多行的输出Hello,World其他的编程语言像C/C++/C#/JA......
  • useradd、usermod、userdel命令详解
    useradduseradd命令用来创建或更新用户信息。-c:加上备注文字,备注文字保存在passwd的备注栏中。-d:指定用户登入时的主目录,替换系统默认值/home/<用户名>-D:变更预设值。-e:指......
  • SQL函数——时间函数
    1、使用NOW()、CURDATE()、CURTIME()获取当前时间在这里我有一个问题想问问大家,你们平时都是怎么样子获取时间的呢?是不是通过手表、手机、电脑等设备了解到的,那么你们......
  • 14. 内存管理
    一、内存的组织方式  程序员编写完程序之后,程序要先加载在计算机的内存中,再运行程序。在C语言中,不同数据在内存中所存储的位置也不一样。全局变量存储在内存中的静态......
  • 【质因数分解算法详解】C/Java/Go/Python/JS/Dart/Swift/Rust等不同语言实现
    关于质因数分解算法的不同语言实现,通过实例来看不同语言的差异什么是质因数算法?即任意一个合数可以分解为多个质数相乘。例如:20=2*2*545=3*3*5210=2*......
  • 2023.03.11.函数重载,引用等
    程序生成的过程:1.预处理:头文件的展开宏的替换预处理指令解析去掉注释2.编译:预处理后文件生成汇编文件.asm(汇编代码)词法解析,语法解析语义分析优化3.汇编:汇编文件进一......
  • 【MySQL-存储引擎,逻辑存储结构,内存,磁盘】
    目录:一、MySQL存储引擎二、InnoDB存储引擎1、InnoDB逻辑存储结构2、InnoDB架构3、InnoDB内存结构4、InnoDB磁盘结构一、MySQL存储引擎1、查看当前版本支持的存储引擎sho......
  • 浙大版《C语言程序设计(第3版)》题目集 习题5-1 符号函数
    本题要求实现符号函数sign(x)。函数接口定义:intsign(intx);其中​​x​​是用户传入的整型参数。符号函数的定义为:若​​x​​大于0,​​sign(x)​​ = 1;若​​x​​等......
  • 浙大版《C语言程序设计(第3版)》题目集 习题5-1 符号函数
    本题要求实现符号函数sign(x)。函数接口定义:intsign(intx);其中​​x​​是用户传入的整型参数。符号函数的定义为:若​​x​​大于0,​​sign(x)​​ = 1;若​​x​​等......