指针转换真的会破坏严格的别名吗?

时间:2017-06-29 03:31:29

标签: c++ c

在以下代码中:

t

严格的别名规则是否会被破坏?据我所知,它确实如此,因为指向短点的指针指向int。但编译器即使编译时也不会抱怨(假设代码在test.c中)

#include <stdio.h>              

int main(void)
{ 
    int n = 74;
    int * pn = &n;

    short * sp;

    //sp = (short *)&n; // <-- bad
    sp = (short *)pn;   // <-- fine

    *sp = 4;
    printf("%d\n", *sp);

    return 0;
}

但是,如果标记为“坏”的行被取消注释,并且标记为“罚款”的行被注释,则会发出抱怨。

有人可以解释一下这个案子真的违反了规则吗?如果是这样,为什么编译器没有检测到它?如果没有,为什么不,因为按照标准应该呢?

2 个答案:

答案 0 :(得分:2)

违反严格别名的不是转换本身,而是违反它的后续无效左值访问(内存重新解释)。

严格别名冲突实际上是运行时违规:它们是通过作为左值访问的对象的动态类型定义的。因此,在编译时并不总是能够捕获严格别名违规。

请考虑以下代码段

short n = 0;
int *pn = (int *) &n;     // 1
short *sp = (short *) pn; // 2
*sp = 4;

此代码不违反严格别名规则,即使它包含与原始代码相同的强制转换(2)。尽管指针pn被声明为int *,但在运行时它实际上指向类型为short的对象。因此,对short *的强制转换以及随后对指针对象的赋值完全有效。 (假设指针值在此往返演员中幸存下来。)

在这样的情况下,编译器显然努力捕获转换链中的第一个转换:看起来是潜在的严格别名违规的相当明显的前提 - 在我的示例中转换为1。 Cast 2具有相当高的产生误报的可能性(如我的例子中所示),并且编译器不会对其发出警告。

答案 1 :(得分:2)

编译器很难在编译时捕获违规,除非您通过解除引用分配一个新值到另一个类型,如下所示。如果您更改了如下所示的代码,您将收到预期的警告。

int main(void)
{ 
    int n = 74; 
    //int * pn = &n;

    //short * sp;

    //sp = (short *)&n; // <-- bad
    //sp = (short *)pn;   // <-- fine

    *((short*)&n) = 4;
    printf("%d\n", n); 

    return 0;
}

此问题显示完全相同的问题,并提供更多详细信息。 GCC: accuracy of strict aliasing warnings

AnT的另一个答案始于以下声明: “这不是违反严格别名的转换本身,而是违反它的后续无效左值访问(内存重新解释)。” .... 那是正确的。我的示例尝试违反规则的左值访问,以便您收到警告。