12

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

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 的函数而不破坏别名规则?

4

1 回答 1

14

C标准对此非常清楚。由 命名的对象的有效类型pvoid*,因为它具有声明的类型,请参见6.5/6。C99中的别名规则适用于读取写入,根据.void*unsigned(1)6.5/7

相反,memcpyof(2)很好,因为unsigned char*可以为任何对象 ( ) 起别名6.5/7。该标准定义memcpy7.21.2/1

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

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

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


根据C++ Standard,我认为这个问题还很不清楚,我认为以下内容成立。请不要将此解释视为唯一可能的解释 - 模糊/不完整的规范留下了很大的猜测空间。

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

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

有一个很长的GCC Bugreport还讨论了通过这种转换产生的指针写入的含义以及与placement-new 的区别是什么(该列表上的人不同意它是什么)。

于 2010-07-18T11:47:09.167 回答