memmove与memcpy的区别

问题引入

在一次基线升级时,需要对不同chip id做适配,比如a chip id需要使用filename 为"bdf_j6a1.bin",b chip id 需要使用filename 为"bdf_j6a1u.bin"。而项目众多 在分chip id 就会显得代码很臃肿。
所以想到了一个方法就是:

  1. 先根据board id 区分不同的项目
  2. 再根据chip id 使用字符串函数memmove对filename进行处理,中间加个’u’

代码

#define BDF_FILE_J6A1 "bdf_j6a1.bin"
#define UMC_CHIP_ID 0x4320
#define BDWLAN_SIZE 8

// 在这之前已经将字符串 “bdf_j6a1.bin” 放在了 filename 这个变量中

if (UMC_CHIP_ID == gdata.chip_info.chip_id) {
    memmove(filename + BDWLAN_SIZE + 1, filename + BDWLAN_SIZE, BDWLAN_SIZE - 1);
    filename[BDWLAN_SIZE] = 'u';
    filename[MAX_FILE_NAME_LEN - 1] = '

memmove与memcpy

memcpy和memmove都是C语言的库函数,相比于strcpy和strncpy只能拷贝字符串数组

这两个函数都是将s2指向位置的n字节数据拷贝到s1指向的位置,区别就在于关键字restrict, memcpy假定两块内存区域没有数据重叠,而memmove没有这个前提条件。如果复制的两个区域存在重叠时使用memcpy,其结果是不可预知的,有可能成功也有可能失败的,所以如果使用了memcpy,程序员自身必须确保两块内存没有重叠部分。

file

正常情况下,即使内容有重叠,src的内容也可以正确地被拷贝到了dest指向的空间

file

这种情况下,src的地址小于dest的地址,拷贝前3个字节没问题,但是拷贝第4,5个字节时,原有的内容已经被src拷贝过来的字符覆盖了,所以已经丢失原来src的内容,这很明显就是问题所在。

memcpy

一般来说,memcpy的实现非常简单,只需要顺序的循环,把字节一个一个从src拷贝到dest就行:

#include <stddef.h> /* size_t */
void *memcpy(void *dest, const void *src, size_t n)
{
    char *dp = dest;
    const char *sp = src;
    while (n--)
        *dp++ = *sp++;
    return dest;
}

memmove

memmove会对拷贝的数据作检查,确保内存没有覆盖,如果发现会覆盖数据,简单的实现是调转开始拷贝的位置,从尾部开始拷贝:

#include <stddef.h> /* for size_t */
void *memmove(void *dest, const void *src, size_t n)
{
    unsigned char *pd = dest;
    const unsigned char *ps = src;
    if (__np_anyptrlt(ps, pd))
        for (pd += n, ps += n; n--;)
            *--pd = *--ps;
    else
        while(n--)
            *pd++ = *ps++;
    return dest;
}

这里__np_anyptrlt是一个简单的宏,用于结合拷贝的长度检测dest与src的位置,如果dest和src指向同样的对象,且src比dest地址小,就需要从尾部开始拷贝。否则就和memcpy处理相同。但是实际在C99实现中,是将内容拷贝到临时空间,再拷贝到目标地址中:

#include <stddef.h> /* for size_t */
#include <stdlib.h> /* for memcpy */

void *memmove(void *dest, const void *src, size_t n)
{
    unsigned char tmp[n];
    memcpy(tmp,src,n);
    memcpy(dest,tmp,n);
    return dest;
}
NOTE:
由此可见memcpy的速度比memmove快一点,如果使用者可以确定内存不会重叠,则可以选用memcpy,否则memmove更安全一些。另外一个提示是第三个参数是拷贝的长度,如果你是拷贝10个double类型的数值,要写成sizeof(double)*10,而不仅仅是10

剑气纵横三万里

“为什么要努力?” “想去的地方很远,想要的东西很贵,喜欢的人很优秀,父母的白发,朋友的约定,周围人的嘲笑,以及,天生傲骨。”

2 Comments

  • memove使用的时候,内存有重叠部分,则从尾位置开始拷贝。这是有什么特殊的考虑吗?同样从首位置开始拷贝不也一样吗?(虽然两种方式的结果不同)

    • @风凉 从首位置拷贝不就出现重叠了嘛?拷贝之前先判断是否有重叠的部分,如果你确定两者没有重叠的部分,memcpy的执行效率更快。如果有重叠部分,memcpy就会报错,所以这时候使用memmove更为安全

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐

暂无内容!
%d 博主赞过: