memcpy 零字节到 const 变量 - 未定义的行为?

在 C 和 C++ 中,当要复制的字节数为零时,将 memcpy 放入 const 变量是否是未定义的行为?

int x = 0;
const int foo = 0;
memcpy( (void *)&foo, &x, 0 );

这个问题不是纯理论的。我有一个调用 memcpy 的场景,如果目标指针指向 const 内存,那么 size 参数保证为零。所以我想知道是否需要将其作为特殊情况处理。

stack overflow memcpy zero bytes into const variable - undefined behavior?
原文答案

答案:

作者头像

c c17

较早的问题 Is it guaranteed to be safe to perform memcpy(0,0,0)? 指出 7.1.4p1:

除非在随后的详细描述中另有明确说明,否则以下每个语句均适用: 如果函数的参数具有无效值(例如函数域之外的值,或程序地址空间之外的指针,或空指针,或 当相应参数不是 const 限定时指向不可修改存储的指针)或具有可变数量参数的函数不期望的类型(提升后),行为未定义。

memcpy 的原型是

void *memcpy(void * restrict s1, const void * restrict s2, size_t n);

其中第一个参数不是 const 限定的,并且 &foo 指向不可修改的存储。所以这个代码是 UB,除非 memcpy 的描述明确说明,否则它没有。它只是说:

memcpy 函数将 n 个字符从 s2 指向的对象复制到 s1 指向的对象中。

这意味着计数为 memcpy0 不会复制任何字符(这也得到了 7.24.1p2 “复制零个字符”的确认,感谢 Lundin),但它并不能免除您通过的要求有效的论据。

作者头像

很明显,在绝大多数平台上,无论源指针和目标指针的有效性如何,将 memcpy(anything,any, 0) 处理为空操作的实现将在各个方面,基本上在每个非- 人为的场景,与做其他任何事情的场景一样好或更好。

然而,标准的编写方式可以解释为指定允许编译器将目标地址与可写存储无关的任何情况视为 UB。

如果使用的实现旨在以标准作者所期望的有用方式处理极端情况,而不考虑标准是否明确要求此类行为,则所有 memcpymemmove 操作的大小为零将被可靠地处理为无操作。如果大小通常为零,则在大小为零的情况下跳过 memcpymemmove 调用可能会带来性能优势,但永远不需要这样的检查来确保正确性。

但是,如果希望确保与编译器配置的可靠兼容性,该编译器配置激进地假设代码永远不会接收触发非标准明确规定的 100% 的极端情况的输入,并且旨在生成无意义的代码,如果接收到这样的输入,那么在任何情况下都需要添加 size==0 检查,如果零大小可能伴随着指向可写存储的指针以外的任何内容,请认识到这样的检查可能会在以下情况下对性能产生负面影响大小很少为零。