【问题标题】:C aliasing rules and memcpyC 别名规则和 memcpy
【发布时间】:2010-07-18 11:41:10
【问题描述】:

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

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

【问题讨论】:

    标签: c++ c strict-aliasing


    【解决方案1】:

    C 标准 对此非常清楚。 p命名的对象的有效类型是void*,因为它有一个声明的类型,见6.5/6。 C99 中的别名规则适用于读取 写入,根据6.5/7,通过unsigned 左值在(1) 中写入void* 是未定义的行为。

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

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

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

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


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

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

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

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

    【讨论】:

    • 请查看对 Marcelo 回答的评论中的问题。有什么相关的吗?
    • @zvrba,哦,没有什么不同。您只需在两者之间使用void* 进行投射,相当于直接投射。如果你想“模拟”memcpy,你必须像unsigned char *pc = (unsigned char*)&p, *i = (unsigned char*)&x; *pc = *x; *pc++ = *x++; ...那样做,它就是这样做的。
    • 如果 srcptr 和 destptr 相等,memcpy() 涉及的对象是否被视为“重叠”,或者是否有任何语言明确允许这种情况?
    猜你喜欢
    • 1970-01-01
    • 2019-01-29
    • 2013-03-11
    • 1970-01-01
    • 2015-05-31
    • 2011-02-15
    • 2018-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多