C别名规则和memcpy

zvr*_*rba 12 c c++ strict-aliasing

在回答另一个问题时,我想到了以下示例:

void *p;
unsigned x = 17;

assert(sizeof(void*) >= sizeof(unsigned));
*(unsigned*)&p = 17;        // (1)
memcpy(&p, &x, sizeof(x));  // (2)
Run Code Online (Sandbox Code Playgroud)

第1行打破了别名规则.然而,第2行是好的.别名规则.问题是:为什么?编译器是否具有关于memcpy等函数的特殊内置知识,还是有一些其他规则可以使memcpy正常运行?有没有办法在标准C中实现类似memcpy的函数而不破坏别名规则?

Joh*_*itb 14

C标准是非常明确.名为的对象的有效类型pvoid*,因为它具有声明的类型,请参阅6.5/6.C99中的别名规则适用于读取写入,而void*通过unsigned左值的写入(1)是根据未定义的行为6.5/7.

与此相反,memcpy(2)是精细的,因为unsigned char*可以别名任何对象(6.5/7).该标准定义了memcpy7.21.2/1

对于本子条款中的所有函数,每个字符都应解释为它具有unsigned char类型(因此每个可能的对象表示都是有效的并且具有不同的值).

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

但是,如果之后存在使用p,则可能会导致取决于bitpattern的未定义行为.如果没有发生这样的使用,那么该代码在C中就可以了.


根据C++标准,我认为这个问题远非明确,我认为以下内容成立.请不要将这种解释作为唯一可能的解释 - 模糊/不完整的规范留下了很大的猜测空间.

线(1)是有问题的,因为对齐&p可能不适合该unsigned类型.它改变存储在该对象的类型punsigned int.只要您以后不再访问该对象p,别名规则就不会被破坏,但对齐要求可能仍然存在.

(2)但是,Line 没有对齐问题,因此只要您p之后不作为a 访问就可以有效,void*这可能会导致未定义的行为,具体取决于void*类型如何解释存储的bitpattern.我不认为对象的类型因此而改变.

有一个很长的GCC Bugreport,也讨论了通过这样一个演员导致的指针写入的含义以及与placement-new的区别(该列表中的人不同意它是什么).