C别名规则和memcpy

时间:2010-07-18 11:41:10

标签: c++ c strict-aliasing

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

void *p;
unsigned x = 17;

assert(sizeof(void*) >= sizeof(unsigned));
*(unsigned*)&p = 17;        // (1)
memcpy(&p, &x, sizeof(x));  // (2)

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

1 个答案:

答案 0 :(得分:14)

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

相比之下,memcpy的{​​{1}}很好,因为(2)可以为任何对象(unsigned char*)添加别名。标准将6.5/7定义为memcpy

  

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

     

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

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


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

p存在问题,因为(1)的{​​{1}}对齐可能不合适。它会将&p中存储的对象类型更改为unsigned。只要您稍后不通过p访问该对象,别名规则就不会中断,但对齐要求可能仍然存在。

unsigned int但没有对齐问题,因此只要您之后不以p访问(2),这可能会导致未定义的行为,具体取决于方式p类型解释存储的bitpattern。我不认为对象的类型因此而改变。

有一个很长的GCC Bugreport也讨论了通过这样一个演员表引起的指针的影响以及与placement-new的区别(该列表上的人不同意它是什么) )。