C语言-内存函数的实现(二)之memmove
C语言中的内存函数有如下这些
- memcpy
- memmove
- memcmp
- memset
下面看看memmove函数
memmove
为什么会需要memmove函数?
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
// 想把12345 拷贝到 34567上去
// 应该打印 1 2 1 2 3 4 5 8 9 10
my_memcpy(arr + 2, arr, 20);
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
// 但是输出 1 2 1 2 1 2 1 8 9 10
return 0;
}
上面会输出 1 2 1 2 1 2 1 8 9 10,我们来看看为什么会出现这样的结果。
我这里画了张图,方便理解。

因为拷贝的地方重叠了,使原来的数据(3 4 5)被覆盖了,导致最后出来的结果不是我们想要的。
也就是说,如果拷贝的地方重叠了,那么就会出现这种情况。
那么如何解决呢?答案就是从后往前拷贝,指针从最后的地方开始操作。
还是上一张图

这样,就得出了我们想要的结果。
但是呢,也不能一概而论,就全部都是从后往前拷贝,还是得分情况的,具体就是看destination和source的位置关系。

回到最开始的问题,为什么会需要memmove函数?,因为memmove这个函数可以处理这种重叠拷贝。
老规矩,我们还是看看文档是怎样说的,如下
void * memmove ( void * destination, const void * source, size_t num );
Move block of memory
移动内存块(移动内存数据)
Copies the values of num bytes from the location pointed by source to the memory block pointed by destination. Copying takes place as if an intermediate buffer were used, allowing the destination and source to overlap.
从source(源内存块位置)直接指向的地方开始复制num个字节的数据到destination指向的内存块位置。然后复制就像使用了中间缓冲区一样,允许destination和source重叠。
The underlying type of the objects pointed by both the source and destination pointers are irrelevant for this function; The result is a binary copy of the data.
这句话没看懂,不影响我们学这个。
The function does not check for any terminating null character in source - it always copies exactly num bytes.
这个函数不会检查'\0',不会遇到'\0'就停下来,它就只认识要复制的num个字节数据。
To avoid overflows, the size of the arrays pointed by both the destination and source parameters, shall be at least num bytes.
为了避免溢出,这两个数组的大小至少为num个字节。
可以看出,memmove和memcpy的唯一区别就是,memmove函数处理的源内存块和目标内存块是可以重叠的。
也就是说,如果源空间和目标空间出现重叠,就得使用memmove函数处理。
实现
断言指针不为空是个好习惯~
void* my_memmove(void* dest, void* src, size_t num)
{
//dest落在了src的左边,从前往后拷贝
//dest落在了src的右边,同时没有超过那个重叠的边界的时候,从后往前拷贝
assert(dest != NULL);
assert(src != NULL);
void* rest = dest;
// void* 不能直接解引用,那么如何复制呢?
// 给了num个字节,也就是需要复制num个字节
// 那就转换成char*,一个一个字节的复制过去
if (dest < src)
//if (dest < src || dest > (char*)src + num)
{
//dest落在了src的左边,从前往后拷
while (num--)
{
*(char*)dest = *(char*)src;
//++(char*)dest;
//++(char*)src;
((char*)dest)++;
((char*)src)++;
}
}
else
{
// 从后往前拷
// 找到最后一个字节
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
return rest;
}
最后,感谢屏幕前的靓仔花宝贵的时间阅读我这篇博客~
本人水平有限,如有错误以及不足之处,欢迎靓仔指出。
C语言-内存函数的实现(二)之memmove的更多相关文章
- C语言-内存函数的实现(一)之memcpy
C语言中的内存函数有如下这些 memcpy memmove memcmp memset 下面看看memcpy函数 memcpy 我们想想,之前有那个字符串拷贝的函数,即strcpy函数.都有拷贝的函数 ...
- C语言内存函数
http://see.xidian.edu.cn/cpp/u/hs3/ 函数 说明 calloc() 分配内存空间 free() 释放内存空间 getpagesize() 取得内存分页大小 mallo ...
- C语言 mmap()函数(建立内存映射) 与 munmap()函数(解除内存映射)
mmap将一个文件或者其它对象映射进内存.文件被映射到多个页上,如果文件的大小不是所有页的大小之和, 最后一个页不被使用的空间将会清零.mmap在用户空间映射调用系统中作用很大. 条件 mmap()必 ...
- Go语言学习——函数二 defer语句
函数 package main import "fmt" // 函数:一段代码的封装 func f1(){ fmt.Println("Hello 中国!") } ...
- 【C语言】函数和自定义函数
函数,我之前也提到过一点点内容.其实函数是很好理解的,但是写起来又十分麻烦. 一. 函数引入 我们知道,C源程序是由函数组成的.请看下面的简单函数例子 #include <stdio.h ...
- c语言之函数指针
一.基础研究 这里研究的内容是函数指针,需要我们在研究后构造程序来描述函数指针数组的用法和向函数传函数指针的方法. 指针有很多种:整型指针.结构体指针.数组指针等等,它们的本质是它们的值都是一个地址, ...
- c语言内存模型
文章一.C语言的内存分配模型 1.程序代码区:存放函数体的二进制代码. 2.全局区数据区:全局数据区划分为三个区域.全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化 ...
- C语言内存四区的学习总结(一)---- 静态区
最近重新学习C语言相关知识,重新提到内存四区的概念,那么在之前的学习的基础上,在这儿做一个简单的总结与分享. 一.内存四区建立的流程 可以简单直观的查看下面的这个图片,直接的说明我们的程序在内存中是如 ...
- [c/c++] programming之路(23)、字符串(四)——strncat,atoi,strcmp,strlen等,以及常用内存函数
一.strncat及自行封装实现 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #i ...
随机推荐
- Go的函数
目录 Go的函数 一.函数的定义 1.函数的基本格式 2.函数的参数 2.1 函数传参的特点:copy传值 3.函数的返回值 4.可变长参数 二.函数的类型 1.给函数的类型重命名 三.匿名函数 1. ...
- Deep Unfolding Network for Image Super-Resolution 论文解读
Introduction 超分是一个在 low level CV 领域中经典的病态问题,比如增强图像视觉质量.改善其他 high level 视觉任务的表现.Zhang Kai 老师这篇文章在我看到的 ...
- pytorch(15)损失函数
损失函数 1. 损失函数概念 损失函数:衡量模型输出与真实标签的差异 \[损失函数(Loss Function): Loss = f(\hat y,y) \] \[代价函数(Cost Function ...
- CodeBlocks的安装配置以及使用教程
CodeBlocks的安装配置以及使用教程 教程写的很啰嗦,本来几句话就能搞定的,但为了照顾到那部分真正的小白还请大家见谅! 一.下载 前往CodeBlocks官网下载带编译器的版本,目前的最新版本为 ...
- 《从零开始TypeScript》系列 - 基础数据类型
TypeScript 是 JavaScript 的超集,这里我们只讨论两者中的不同的部分,或者需要注意的部分 数组 Array:在TypeScript中,有两种方式来定义一个数组: 在元素类型后面接上 ...
- Graylog日志管理单机部署、日志节点的Sidecar配置以及简单的警告事件邮件发送
应该是上个星期的上个星期,下了个任务,要做Graylog的部署以及文档,emmm....带log,肯定是和日志有关系了呗,不过也没听过啊,去搜了一下,确实,也不少帖子博客相关的都有安装部署,还是yum ...
- CVE-2017-12149-JBoss 5.x/6.x 反序列化
漏洞分析 https://www.freebuf.com/vuls/165060.html 漏洞原理 该漏洞位于JBoss的HttpInvoker组件中的 ReadOnlyAccessFilter 过 ...
- Java实现解压缩文件和文件夹
一 前言 项目开发中,总会遇到解压缩文件的时候.比如,用户下载多个文件时,服务端可以将多个文件压缩成一个文件(例如xx.zip或xx.rar).用户上传资料时,允许上传压缩文件,服务端进行解压读取每一 ...
- Java流程控制:三种基本结构
顺序结构: Java的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行顺序结构是最简单的算法结构语句与语句之间,框与框之间是按从上到下的顺序进行的,它是由若干个依次执行的处理步骤组成的, ...
- Redis持久化操作RDB和AOF 对比于HDFS的SecondaryNode
写在前面的话 最近学习比较多流行的大数据框架和完成两个大数据项目后,又突然学起了Redis.之所以之前的框架不学习记录呢,是因为之前的学习都是为了完成参加服创比赛的项目所以时间较紧,现在基本架构和编码 ...