代码之家  ›  专栏  ›  技术社区  ›  zvrba

C别名规则和memcpy

  •  12
  • zvrba  · 技术社区  · 14 年前

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

    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 回复  |  直到 14 年前
        1
  •  14
  •   Johannes Schaub - litb    14 年前

    这个 C标准 很清楚。由命名的对象的有效类型 p void* ,因为它具有声明的类型,请参见 6.5/6 . c99中的别名规则适用于读取 写入,写入 空洞* 通过一个 unsigned 左值 (1) 未定义的行为 6.5/7 .

    相比之下, memcpy 属于 (2) 很好,因为 unsigned char* 可以为任何对象创建别名( 5.5/7 )本标准规定 曼皮西 7.21.2/1 作为

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

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

    但是,如果存在使用 然后,根据位模式,这可能会导致未定义的行为。如果这样的使用没有发生,那么C语言中的代码就可以了。


    根据 C++标准 在我看来,在这个问题上还不清楚,我认为以下观点是成立的。请不要将这种解释视为唯一可能的解释——含糊不清/不完整的规范给猜测留下了很大的空间。

    线 (1) 是有问题的,因为 &p 可能不适合 未签名的 类型。它更改存储在中的对象的类型 成为 unsigned int . 只要你以后不通过 ,别名规则未被破坏,但对齐要求可能仍然存在。

    线 (2) 但是没有对齐问题,因此只要您不访问就有效 后来作为 空洞* ,这可能导致未定义的行为,具体取决于 空洞* 类型解释存储的位模式。我认为物体的类型不会因此而改变。

    有一个很长的 GCC Bugreport 这还讨论了通过这样的强制转换产生的指针进行写入的含义,以及新位置的不同之处(列表中的人不同意这一点)。