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.