Namespaces
Variants

memmove, memmove_s

From cppreference.net
< 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 数组末尾,则行为未定义。若 dest src 为无效指针或空指针,则行为未定义。
2) (1) 相同,但在运行时检测到以下错误时,会将整个目标范围 [ dest, dest + destsz ) 清零(若 dest destsz 均有效),并调用当前安装的 约束处理函数
  • dest src 为空指针
  • destsz count 大于 RSIZE_MAX
  • count 大于 destsz (将发生缓冲区溢出)
dest 所指向的字符数组大小满足 count < destsz 时行为未定义;换言之, destsz 的错误值不会暴露即将发生的缓冲区溢出。
与所有边界检查函数相同,仅当实现定义了 __STDC_LIB_EXT1__ 且用户在包含 <string.h> 前将 __STDC_WANT_LIB_EXT1__ 定义为整型常量 1 时,才保证 memmove_s 可用。

目录

参数

dest - 指向目标复制对象的指针
destsz - 目标对象中允许修改的最大字节数(通常为目标对象的尺寸)
src - 指向源复制对象的指针
count - 需要复制的字节数

返回值

1) 返回 dest 的副本
2) 成功时返回零,错误时返回非零值。若发生错误且 dest 非空指针且 destsz 有效时,向目标数组写入 destsz 个零字节。

注释

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

尽管规范描述"如同"使用了临时缓冲区,但此函数的实际实现不会产生开销、双重拷贝或额外内存占用。常见实现方案(glibc和bsd libc)是:当目标地址在源地址之前时从缓冲区起始处向前拷贝字节,反之则从末尾向后拷贝,若不存在任何重叠时则回退至更高效的 memcpy 实现。

严格别名规则 禁止以两种不同类型值的形式检查同一内存时,可使用 memmove 进行值转换。

示例

#define __STDC_WANT_LIB_EXT1__ 1
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.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); // 正确
    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); // 计数值大于目标大小
    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

参考文献

  • C23 标准 (ISO/IEC 9899:2024):
  • 7.24.2.2 memmove 函数 (页: TBD)
  • K.3.7.1.2 memmove_s 函数 (页: TBD)
  • C17 标准 (ISO/IEC 9899:2018):
  • 7.24.2.2 memmove 函数 (p: 264)
  • K.3.7.1.2 memmove_s 函数 (p: 446)
  • C11 标准 (ISO/IEC 9899:2011):
  • 7.24.2.2 memmove 函数 (页: 363)
  • K.3.7.1.2 memmove_s 函数 (页: 615)
  • C99标准(ISO/IEC 9899:1999):
  • 7.21.2.2 memmove函数(页码:326)
  • C89/C90 标准 (ISO/IEC 9899:1990):
  • 4.11.2.2 memmove 函数

参见

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