C17 说以下内容memcpy
[7.24.2.1p2]:
memcpy函数将s2指向的对象中的n个字符复制到指向的对象中
通过 s1。如果复制发生在objects重叠,行为是未定义的。
常见的解释是您不能复制重叠的内存区域。但这并不完全相同,因为同一对象中可能存在不重叠的区域。
想象一个典型的实现,其中sizeof(unsigned int) == 4
and sizeof(unsigned short) == 2
。为了简单起见,进一步假设没有陷阱表示,并且alignof(unsigned short) <= alignof(unsigned int)
。考虑:
unsigned int x = 0xdeadbeef;
unsigned short *p = (unsigned short *)&x;
memcpy(&x, p+1, 2);
解释#1:我在之间复制x
and 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
将由实现定义并且可以是0xdeaddead
or 0xbeefbeef
或者是其他东西。)
哪种解释是正确的(或者都不正确)?
如果我改为写有什么区别吗memcpy(p, p+1, 2)
,在这种情况下,我可以说是在不重叠的对象之间复制p[1]
and 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);
所有相同的论点都应该适用。
(这个问题是后续的ixSci 对另一个问题的评论 https://stackoverflow.com/questions/72334954/why-does-memcpy-cause-compilers-to-seemingly-forget-about-strict-aliasing#comment127804450_72334954.)