原 memmove 和 memcpy的区别

memcpy memmove 都是C语言中的库函数,在头文件string.h中,作用是拷贝一定长度的内存的内容,原型分别如下: void *memcpy(void *dst, const void *src, size_t count); void *memmove(void *dst, const void *src, size_t count);  他们的作用是一样的,唯一的区别是,当内存发生局部重叠的时候,memmove保证拷贝的结果是正确的,memcpy不保证拷贝的结果的正确。

第一种情况下,拷贝重叠的区域不会出现问题,内容均可以正确的被拷贝。 第二种情况下,问题出现在右边的两个字节,这两个字节的原来的内容首先就被覆盖了,而且没有保存。所以接下来拷贝的时候,拷贝的是已经被覆盖的内容,显然这是有问题的。 实际上,memcpy只是memmove的一个子集。 memmove在copy两个有重叠区域的内存时可以保证copy的正确,而memcopy就不行了,但memcopymemmove的速度要快一些,如: char s[] = "1234567890"; char* p1 = s; char* p2 = s+2;

memcpy(p2, p1, 5)与memmove(p2, p1, 5)的结果就可能是不同的,memmove()可以将p1的头5个字符"12345"正确拷贝至p2,而memcpy()的结果就不一定正确了 关于memmove的实现:

void *mymemmove(void *dest, const void *src, size_t n)
{
    char temp[n];
    int i;
    char *d = dest;
    const char *s = src;

    for (i = 0; i < n; i++) 
        temp[i] = s[i];
    for (i = 0; i < n; i++) 
        d[i] = temp[i];

    return dest;
}

关于memcpy的实现:

void *mymemcpy(void *dest, const void *src, size_t n)
{
    char *d = dest;
    const char *s = src;
    int *di;
    const int *si;
    int r = n % 4;
    while (r--)
        *d++ = *s++;
    di = (int *)d;
    si = (const int*)s;
    n /= 4;
    while (n--)
        *di++ = *si++;
    return dest;
}

下面再通过一个例子进行描述:

#include <string.h>
 
void *memmove2(void *dest, const void *src, size_t n)
{
    char temp[n];
    int i;
    char *d = dest;
    const char *s = src;
    for (i = 0; i < n; i++)
        temp[i] = s[i];

    for (i = 0; i < n; i++) 
    {
       d[i] = temp[i];
	printf("the src is %s\n", src);
	printf("the dest is %s\n", dest);
	printf("the temp is %s\n", temp);

    }
    return dest;
}

void *memcpy2(void *dest, const void *src, size_t n)
{
    char *d = dest;
    const char *s = src;
    int *di;
    const int *si;
    int r = n % 4;
    
    while (r--)
        *d++ = *s++;
    di = (int *)d;
    si = (const int*)s;
    n /= 4;
    while (n--)
        *di++ = *si++;

    return dest;
}
 
int
main(int argc, char **argv)
{
    char s[] = "1234567890";
    char* p1 = s;
    char* p2 = s+2;
    char s1[] = "1234567890";
    char* p11 = s1;
    char* p21 = s1+2;
	
    memcpy2(p2, p1, 5);
    memmove2(p21, p11, 5);

    printf("the p2 is %s\n", p2);
    printf("the p1 is %s\n", p1);
    printf("the s is %s\n", s);
    printf("the p21 is %s\n", p21);
    printf("the p11 is %s\n", p11);
    printf("the s1 is %s\n", s1);

    return 0;
}

 输出结果为:

the src is 1214567890
the dest is 14567890
the temp is 12345
the src is 1212567890
the dest is 12567890
the temp is 12345
the src is 1212367890
the dest is 12367890
the temp is 12345
the src is 1212347890
the dest is 12347890
the temp is 12345
the src is 1212345890
the dest is 12345890
the temp is 12345
the p2 is 12145890
the p1 is 1212145890
the s is 1212145890
the p21 is 12345890
the p11 is 1212345890
the s1 is 1212345890

这里边memcpy确实发生了错误,由于是发生了重叠,其次memcpy又是对于部分数据以其他形式进行赋值,所以在一定情况下,结果就不一定正确了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏GreenLeaves

C#核编之系统数据类型和相应的C#关键字

和任何编程语言一样,C#定义了一组用于表示局部变量、成员变量、返回值以及输入参数的基本数据类型。然而,与其他编程语言不同的是,这些关键字不只是编译器能识别的标记...

20680
来自专栏电光石火

PHP获取时间戳的毫秒

php获取时间的方式是time(); 那么如果是涉及需要精细的时间的应用,那么怎么获取呢? /** 获取当前时间戳,精确到毫秒 */ functi...

47490
来自专栏黑泽君的专栏

代码块:在Java中用{}括起来的代码

 (1)在Java中用{}括起来的代码。 (2)代码块分类:(根据其位置和声明的不同) A:局部代码块       在方法定义中,用于限定变量的生命...

9110
来自专栏程序员同行者

python split()函数使用拆分字符串 将字符串转化为列表

28950
来自专栏Android Note

Kotlin —  Destructuring Declarations(解构声明)

13520
来自专栏Python小屋

详解Python对象属性

在面向对象编程中,公开的数据成员可以在外部随意访问和修改,很难控制用户修改时新数据的合法性。解决这一问题的常用方法是定义私有数据成员,然后设计公开的成员方法来提...

31780
来自专栏py+selenium

python爬虫笔记之re.IGNORECASE

       re.IGNORECASE有什么用?re.IGNORECASE是什么意思?(原谅我抓下seo。。)

10720
来自专栏海天一树

小朋友学Python(3):布尔类型

本节讲解C/C++/Java/Python中的布尔类型。 一、C语言 C语言中没有布尔类型,判断时 ,0为假,非0为真。 二、C++ C++的bool是布尔类型...

295110
来自专栏青青天空树

2017-统计字符个数

输入:输入数据有多行,第一行是一个整数n,表示测试实例的个数,后面跟着n行,每行包括一个由字母和数字组成的字符串。

12610
来自专栏C语言及其他语言

[蓝桥杯]Hello, world!

题目描述 This is the first problem for test. Since all we know the ASCII code, your ...

35580

扫码关注云+社区

领取腾讯云代金券