memmove, memmove_s

来自cppreference.com
< c‎ | string‎ | byte
在标头 <string.h> 定义
void* memmove( void* dest, const void* src, size_t count );
(1)
errno_t memmove_s(void *dest, rsize_t destsz, const void *src, rsize_t count);
(2) (C11 起)
1)src 所指向的对象复制 count 个字节到 dest 所指向的对象。两个对象都被转译成 unsigned char 的数组。对象可以重叠:如同复制字符到临时数组,再从该数组到 dest 一般进行复制。
若在 dest 数组末尾之后发生访问则行为未定义。若 destsrc 为非法或空指针则行为未定义。
2)(1),但错误时清零整个范围 [dest, dest+destsz)(若 destdestsz 均合法)。它在运行时检测下列错误,并调用当前安装的约束处理函数
  • destsrc 为空指针
  • destszcount 大于 RSIZE_MAX
  • count 大于 destsz(会出现溢出)
dest 所指向的字符数组大小 < count <= destsz 则行为未定义;换言之,destsz 的错误值不暴露行将发生的缓冲区溢出。
同所有边界检查函数,memmove_s,仅若实现定义 __STDC_LIB_EXT1__ 且用户在包含 <string.h> 前定义 __STDC_WANT_LIB_EXT1__ 为整数常量 1 才保证可用。

参数

dest - 指向复制目标对象的指针
destsz - 要于目标修改的最大字节数(典型地为目标对象的大小)
src - 指向复制来源对象的指针
count - 要复制的字节数

返回值

1) 返回 dest 的副本,本质为更底层操作的临时内存地址,在实际操作中不建议直接使用此地址,操作完成以后,真正有意义的地址是dest本身。
2) 成功时返回零,失败时返回非零值。在失败时,若 dest 不是空指针且 destsz 合法,则亦会写入 destsz 个零字节到目标数组。

注解

memmove 可用于设置由分配函数获得的对象的有效类型

尽管说明了“如同”使用临时缓冲区,此函数的实际实现不会带来二次复制或额外内存的开销。常用方法(glibc 和 bsd libc)是若目标在源之前开始,则从缓冲区开始正向复制,否则从末尾反向复制,完全无重叠时回落到更高效的 memcpy

严格别名时用禁止检验同一内存为二个不同类型的值时,可使用 memmove 转换值。

示例

#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
 
int main(void)
{
    char str[] = "1234567890";
    puts(str);
    memmove(str+4, str+3, 3); // 从 [4,5,6] 复制到 [5,6,7]
    puts(str);
 
    // 设置分配的内存的有效类型为 int
    int *p = malloc(3*sizeof(int));   // 分配的内存无有效类型
    int arr[3] = {1,2,3};
    memmove(p,arr,3*sizeof(int));     // 分配的内存现在拥有有效类型
 
    // 转译数据
    double d = 0.1;
//    int64_t n = *(int64_t*)(&d); // 严格别名使用违规
    int64_t n;
    memmove(&n, &d, sizeof d); // OK
    printf("%a is %" PRIx64 " as an int64_t\n", d, n);
 
#ifdef __STDC_LIB_EXT1__
    set_constraint_handler_s(ignore_handler_s);
    char src[] = "aaaaaaaaaa";
    char dst[] = "xyxyxyxyxy";
    int r = memmove_s(dst,sizeof dst,src,5);
    printf("dst = \"%s\", r = %d\n", dst,r);
    r = memmove_s(dst,5,src,10);            //  count 大于 destsz  
    printf("dst = \"");
    for(size_t ndx=0; ndx<sizeof dst; ++ndx) {
        char c = dst[ndx];
        c ? printf("%c", c) : printf("\\0");
    }
    printf("\", r = %d\n", r);
#endif
}

可能的输出:

1234567890
1234456890
0x1.999999999999ap-4 is 3fb999999999999a as an int64_t
dst = "aaaaayxyxy", r = 0
dst = "\0\0\0\0\0yxyxy", r = 22

引用

  • C17 标准(ISO/IEC 9899:2018):
  • 7.24.2.2 The memmove function (第 264 页)
  • K.3.7.1.2 The memmove_s function (第 446 页)
  • C11 标准(ISO/IEC 9899:2011):
  • 7.24.2.2 The memmove function (第 363 页)
  • K.3.7.1.2 The memmove_s function (第 615 页)
  • C99 标准(ISO/IEC 9899:1999):
  • 7.21.2.2 The memmove function (第 326 页)
  • C89/C90 标准(ISO/IEC 9899:1990):
  • 4.11.2.2 The memmove function

参阅

将一个缓冲区复制到另一个
(函数)
在两个可能重叠的数组间复制一定数量的宽字符
(函数)