严格的别名和覆盖继承

PSk*_*cik 9 c struct strict-aliasing language-lawyer

考虑以下代码示例:

#include <stdio.h>

typedef struct A A;

struct A {
   int x;
   int y;
};

typedef struct B B;

struct B {
   int x;
   int y;
   int z;
};

int main()
{
    B b = {1,2,3};
    A *ap = (A*)&b;

    *ap = (A){100,200};      //a clear http://port70.net/~nsz/c/c11/n1570.html#6.5p7 violation

    ap->x = 10;  ap->y = 20; //lvalues of types int and int at the right addrresses, ergo correct ?

    printf("%d %d %d\n", b.x, b.y, b.z);
}
Run Code Online (Sandbox Code Playgroud)

我曾经认为像将B*转换为A*并使用A*来操纵B*对象这样的事情是严格的别名违规.但后来我意识到标准真的只需要:

对象的存储值只能由具有以下类型之一的左值表达式访问:1)与对象的有效类型兼容的类型,(...)

和表达式ap->x如果有正确的类型和地址,ap那里的类型应该不重要(或者它是什么?).在我看来,这意味着只要子结构不作为一个整体被操纵,这种类型的重叠继承就是正确的.

这种解释是否有缺陷或表面上与标准作者的意图不一致?

M.M*_*M.M 10

行有*ap =一个严格的别名冲突:类型的对象B是使用类型的左值表达式编写的 A.

假设该线不存在,我们继续前进ap->x = 10; ap->y = 20;.在这种情况下,类型的左值int用于写入类型的对象int.

关于这是否是严格的别名违规,存在分歧.我认为标准的信件说它不是,但是其他人(包括gcc和clang开发人员)认为ap->x暗示*ap被访问了.大多数人都同意标准对严格别名的定义太模糊,需要改进.

使用结构定义的示例代码:

void f(A* ap, B* bp)
{
  ap->x = 213;
  ++bp->x;
  ap->x = 213;
  ++bp->x;
}

int main()
{
   B b = { 0 };
   f( (A *)&b, &b );
   printf("%d\n", b.x);
}
Run Code Online (Sandbox Code Playgroud)

对于我这种输出214-O2,并2-O3与海湾合作委员会.在godbolt上为gcc 6.3生成的程序集是:

f:
    movl    (%rsi), %eax
    movl    $213, (%rdi)
    addl    $2, %eax
    movl    %eax, (%rsi)
    ret
Run Code Online (Sandbox Code Playgroud)

这表明编译器已将函数重新排列为:

int temp = bp->x + 2;
ap->x = 213;
bp->x = temp;
Run Code Online (Sandbox Code Playgroud)

因此编译器必须考虑ap->x可能不是别名bp->x.

  • @ 2501评论不适用于扩展讨论 - 我不想在这里就严格别名的解释进行辩论.在过去的评论辩论中,从未在过去的评论辩论中解决了解释标准中含糊不清的论据,而这一论点在过去的评论辩论中并未得到解决.这种辩论更适合讨论论坛,例如usenet或reddit.也许你可以发布一个详细解释你的"证据"的答案,然后其他读者可以形成自己的结论. (2认同)