考虑下面的代码示例:

#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);
}

我曾经认为将B *强制转换为A *并使用A *操纵B *对象之类的行为是严格的别名冲突。
但是后来我意识到该标准实际上只要求:



和诸如ap->x之类的表达式确实具有正确的类型和地址,并且ap的类型在那儿实际上并不重要(或者是吗?)。在我看来,这暗示着这种覆盖继承是正确的,只要未对整个子结构进行操作即可。

这种解释是有缺陷的还是表面上与标准作者的意图背道而驰?

最佳答案

带有*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);
}

对我来说,它使用gcc在214处输出-O2,在2处输出-O3
针对gcc 6.3在godbolt上生成的程序集为:
f:
    movl    (%rsi), %eax
    movl    $213, (%rdi)
    addl    $2, %eax
    movl    %eax, (%rsi)
    ret

这表明编译器已将函数重新安排为:
int temp = bp->x + 2;
ap->x = 213;
bp->x = temp;

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

关于c - 严格的别名和覆盖继承,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42352681/

10-12 03:06