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)被覆盖了,导致最后出来的结果不是我们想要的。

也就是说,如果拷贝的地方重叠了,那么就会出现这种情况。

那么如何解决呢?答案就是从后往前拷贝,指针从最后的地方开始操作。

还是上一张图

这样,就得出了我们想要的结果。

但是呢,也不能一概而论,就全部都是从后往前拷贝,还是得分情况的,具体就是看destinationsource的位置关系。

回到最开始的问题,为什么会需要memmove函数?,因为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的更多相关文章

  1. C语言-内存函数的实现(一)之memcpy

    C语言中的内存函数有如下这些 memcpy memmove memcmp memset 下面看看memcpy函数 memcpy 我们想想,之前有那个字符串拷贝的函数,即strcpy函数.都有拷贝的函数 ...

  2. C语言内存函数

    http://see.xidian.edu.cn/cpp/u/hs3/ 函数 说明 calloc() 分配内存空间 free() 释放内存空间 getpagesize() 取得内存分页大小 mallo ...

  3. C语言 mmap()函数(建立内存映射) 与 munmap()函数(解除内存映射)

    mmap将一个文件或者其它对象映射进内存.文件被映射到多个页上,如果文件的大小不是所有页的大小之和, 最后一个页不被使用的空间将会清零.mmap在用户空间映射调用系统中作用很大. 条件 mmap()必 ...

  4. Go语言学习——函数二 defer语句

    函数 package main import "fmt" // 函数:一段代码的封装 func f1(){ fmt.Println("Hello 中国!") } ...

  5. 【C语言】函数和自定义函数

    函数,我之前也提到过一点点内容.其实函数是很好理解的,但是写起来又十分麻烦. 一.     函数引入 我们知道,C源程序是由函数组成的.请看下面的简单函数例子 #include <stdio.h ...

  6. c语言之函数指针

    一.基础研究 这里研究的内容是函数指针,需要我们在研究后构造程序来描述函数指针数组的用法和向函数传函数指针的方法. 指针有很多种:整型指针.结构体指针.数组指针等等,它们的本质是它们的值都是一个地址, ...

  7. c语言内存模型

    文章一.C语言的内存分配模型 1.程序代码区:存放函数体的二进制代码. 2.全局区数据区:全局数据区划分为三个区域.全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化 ...

  8. C语言内存四区的学习总结(一)---- 静态区

    最近重新学习C语言相关知识,重新提到内存四区的概念,那么在之前的学习的基础上,在这儿做一个简单的总结与分享. 一.内存四区建立的流程 可以简单直观的查看下面的这个图片,直接的说明我们的程序在内存中是如 ...

  9. [c/c++] programming之路(23)、字符串(四)——strncat,atoi,strcmp,strlen等,以及常用内存函数

    一.strncat及自行封装实现 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #i ...

随机推荐

  1. DRF 三大认证的配置及使用方法

    目录 三大认证 一.身份认证 1.身份认证配置 1.1 全局配置身份认证模块 1.2 局部配置身份认证模块 2.drf提供的身份认证类(了解) 3.rf-jwt提供的身份认证类(常用) 4.自定义身份 ...

  2. Django 自定义标签与过滤器报错 No module named 'templatetags'

    Django 自定义标签与过滤器报错 按照网上的教程如果想使用自定义的标签与过滤器就得往settings.py中添加下列数据 TEMPLATES = [ { 'BACKEND': 'django.te ...

  3. Linux安全模型中的3A

    3A Authentication : 认证 验证用户身份 Authorization : 授权 不同用户设置不同权限 Accouting | Audition : 审计 Linux 验证用户身份 U ...

  4. CSS基础 和 font字体、背景属性连写 与 清除浮动方法

    1.伪类 1. :link 2. :visited 3. :hover(重要) 4. :active 5. :focus(input标签获取光标焦点) 2.伪元素 1.:first-letter 2. ...

  5. 数据库索引知识到MySQL InnoDB

    前言 本文聊聊数据库中的索引,涉及索引基础数据结构,分类.以及使用索引的缺点. 索引就像一本书的目录,商场里面各个楼层指示图,让我们不需要自己无目的的找,而是能够很快的找到自己想要的. 1. 索引的基 ...

  6. WPF 基础 - 控件与布局

    1. 前言 1. 数据驱动 UI WPF 是数据核心.主动的,UI 从属数据并表达数据.是被动的: 不再是 UI 驱动数据,而是数据驱动 UI: 2. 控件的定义 控件.数据内容.行为(控件响应用户操 ...

  7. 自定义校验注解ConstraintValidator

    一 前言 系统执行业务逻辑之前,会对输入数据进行校验,检测数据是否有效合法的.所以我们可能会写大量的if else等判断逻辑,特别是在不同方法出现相同的数据时,校验的逻辑代码会反复出现,导致代码冗余, ...

  8. Python:垃圾回收

    有很多不同的方法来实现垃圾回收,例如跟踪,引用计数,转义分析,时间戳和心跳信号等.不同的语言依赖于不同的垃圾回收实现,例如,有些将其与编译器和运行时系统集成在一起.而其他语言则可能需要事后设置,甚至可 ...

  9. Python装饰器(3)

    这篇文章中记录说明下多个装饰器一同装饰同一个函数时的执行顺序问题. [装饰器链] 按照惯例,先看代码示例: import time def debug1(str): #传参接受类的方法 def fun ...

  10. c/c++ switch case内括号

    如果在case语句中有定义变量,则必须要加{},否则会报错.