你能在同一对象的非重叠区域之间进行memcpy吗?

Nat*_*dge 9 c memcpy language-lawyer

memcpyC17 关于[7.24.2.1p2]做了以下说明:

memcpy 函数将 s2 指向的对象中的 n 个字符复制到 s1 指向的对象中。如果复制发生在重叠的对象之间,则行为未定义。

常见的解释是您不能复制重叠的内存区域。但这并不完全相同,因为同一对象中可能存在不重叠的区域。

想象一下一个典型的实现,其中sizeof(unsigned int) == 4sizeof(unsigned short) == 2。为了简单起见,进一步假设没有陷阱表示,并且alignof(unsigned short) <= alignof(unsigned int)。考虑:

unsigned int x = 0xdeadbeef;
unsigned short *p = (unsigned short *)&x;
memcpy(&x, p+1, 2);
Run Code Online (Sandbox Code Playgroud)

解释#1:我在x和 之间复制x,对象x当然与自身重叠,所以我导致了 UB。

解释#2:由于<string.h>函数操作“被视为字符类型数组的对象”[7.24.1p1],因此我实际上将其视为x数组unsigned char[4],并且我只是将该数组的元素 2 和 3 复制到元素 0 和 1。这些unsigned char对象不以任何方式重叠,所以我没有造成UB。我得到了和我一样的效果unsigned char *q = (unsigned char *)&x; q[0] = q[2]; q[1] = q[3];。(当然, 的结果值x将由实现定义,并且可以是0xdeaddead0xbeefbeef或其他值。)

哪种解释是正确的(或者都不正确)?

如果我改写memcpy(p, p+1, 2),这有什么区别吗?在这种情况下,我可以说是在非重叠对象p[1]和之间进行复制p[0]


unsigned short如果上例中的使用有问题,请考虑改为

unsigned int x = 0xdeadbeef;
unsigned char *p = (unsigned char *)&x;
memcpy(&x, p+2, 2);
// or
memcpy(p, p+2, 2);
Run Code Online (Sandbox Code Playgroud)

所有相同的论点都应该适用。


(这个问题是ixSci 对另一个问题的评论的后续。)