C语言库函数 Memcpy 和 Memmove 的区别,你知道多少?

开发 后端
memcpy和memmove都是 C 语言的库函数,相比于 strcpy和 strncpy只能针对于字符类型的数组(),这两个函数可以拷贝其他类型的数组,对于 memcpy和 memmove的区别是什么呢?

 

[[414269]]

 

基本概念阐述

memcpy和memmove都是 C 语言的库函数,相比于 strcpy和 strncpy只能针对于字符类型的数组(),这两个函数可以拷贝其他类型的数组,对于 memcpy和 memmove的区别是什么呢?这里,在 Linux 里通过 man命令查看两个函数的区别,查询的结果如下所示,首先是 memcpy函数的阐述。

通过上述信息,可以知道,函数原型是:

  1. void *memcpy(void *dest, const void *src, size_t n); 

这个函数的功能如上面所说,就是复制src存储区域 n个字节到dest区域,并且src和dest的内存区域不能够重叠。

紧接着来看memmove函数,同样的,来看Linux里的帮助手册:

通过上述信息,可以知道,对于memmove的函数原型是:

  1. void *memmove(void *dest, const void *src, size_t n); 

具体函数是什么意思呢?通过上图中的DESCRIPTION可以看到:

memmove() 函数将 n 个字节从内存区域 src 复制到内存区域 dest, 但是相比于memcpy函数不同的是,他的内存区域可能会重叠:复制的过程就好比是将 src 中的字节首先被复制到一个不重叠的临时数组中src 或 dest中,然后将字节从临时数组复制到 dest。

实现 memcpy 和 memmove及原理介绍

关于前面所叙述的内存重叠的情况,会出现哪些问题呢?在论述这个问题之前,我们先来自己实现 memcpy 和 memmove 函数,当然自己实现的大多数情况是没有库实现的那么严谨和完备的。首先是memcpy函数的实现:

  1. void *memcpy(void *dest, const void *src, size_t count)   
  2. {   
  3.     if(dest == NULL || src == NULL || count <= 0)  return NULL;   
  4.     char *d = (char *)dest;   
  5.     char *s = (char *)src;   
  6.     while(count--)   
  7.     {   
  8.         *d++ = *s++;   
  9.     }   
  10.     return dest;   
  11. }   

代码很容易理解,就不在这里进行赘述了,其中,有一点也是笔者自己以前容易遗忘的一点,就是函数入口处对参数进行检查,不然会发生意想不到的错误。

接下来就是 memmove函数的实现:

  1. void *memmove(void *dest, const void *src, size_t count)   
  2. {   
  3.     if(dest == NULL || src == NULL || count <= 0)  return NULL;   
  4.     if(dest < src)   
  5.     {   
  6.         char *d = (char *)dest;   
  7.         char *s = (char *)src;   
  8.         while (count--)   
  9.         {   
  10.             *d++ = *s++;   
  11.         }   
  12.     }   
  13.     else   
  14.     {   
  15.         char *d = (char *)dest + count;   
  16.         char *s = (char *)src + count;   
  17.         while (count--)   
  18.         {   
  19.             *--d = *--s;   
  20.         }   
  21.     }       
  22.     return dest;   
  23. }   

memmove 函数要相比于 memcpy函数的实现要复杂一点点:分成了目的地址在前还是在后两种情况,如果是目的地址在前,那么就必须将src地址所在的字符串从前往后拷贝,反之,则必须将src所在的字符串从后往前拷贝。

如何解释这一原因呢,我们从一个例子说起,下面是对应的代码:

  1. int main(int argc, char **argv)  
  2.     int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; 
  3.     int i = 0; 
  4.     my_memcpy(arr + 2, arr, 20); 
  5.  
  6.     for (i = 0; i < 10; i++)  
  7.     { 
  8.         printf("%d ", arr[i]); 
  9.     } 
  10.  
  11.     return 0; 

可以看到代码所实现的功能是,将arr数组中12345拷贝到 34567所在的地址中去,按照这样一个思路,因该输出的是:

  1. 1 2 1 2 3 4 5 8 9 10 

但是程序运行后输出的是:

  1. 1 2 1 2 1 2 1 8 9 10 

这是为什么呢?笔者这里来图解一下:

首先,将src地址的值赋值给dest,然后指针后移动,继续下一次的赋值,此时数据就发生了变化,如下图所示:

可以看到,此时 3 的位置变成了 1,继续移动指针,也就有了如下的变化:

我们依据此原理,最后再移动三次指针,也就是如下所示的变化:

最终也就得到了上述的结果。

这种情况也就是dest在后,然后src在前的一种情况,如果是从前往后拷贝的话,也就会造成上述的问题,而解决的办法就是从后往前拷贝,具体的过程,也如下图所示:

可见,如果是此时 dest的地址在src的后面,那么就需要从后往前复制,这样才不会导致数据覆盖掉。

额外注意的一点,上文也提到了,就是说,对于 memmove 也不是一概而论的,如果是 dest的地址在前面,那么也还是需要从前往后复制才行。

至此,关于 memmove和 memcpy 的内容就叙述完啦~

本文转载自微信公众号「wenzi嵌入式软件」,可以通过以下二维码关注。转载本文请联系wenzi嵌入式软件公众号。

 

责任编辑:武晓燕 来源: wenzi嵌入式软件
相关推荐

2022-08-05 13:38:08

C语言库函数printf()

2017-09-26 11:43:12

Java异常和处理

2022-12-16 15:20:19

RustC 语言

2021-07-11 18:04:04

C语言

2024-01-01 08:25:53

ViewSurface框架

2022-07-01 11:56:54

C语言C++编程语言

2020-08-05 12:17:00

C语言代码分配

2024-05-06 00:30:00

MVCC数据库

2022-08-11 08:46:23

索引数据结构

2018-01-02 09:31:12

大数据数据互联网

2019-05-08 16:00:48

人工智能人脸识别刷脸

2021-07-26 23:57:48

Vuex模块项目

2023-08-28 07:39:49

线程调度基本单位

2024-04-16 16:20:46

2022-04-07 16:03:36

JavaScriptTypeScript

2023-06-01 08:15:04

CentOS红帽

2015-07-31 13:38:39

FreeBSDLinux

2020-05-15 14:34:16

C语言丹尼斯 · 里奇开发者

2022-03-23 15:36:13

数字化转型数据治理企业

2023-08-02 08:14:33

监控MTS性能
点赞
收藏

51CTO技术栈公众号