严格的别名和灵活的数组成员

时间:2015-09-29 00:01:24

标签: c arrays strict-aliasing

我以为我很了解C,但我对以下代码感到困惑:

typedef struct {
    int type;
} cmd_t;

typedef struct {
    int size;
    char data[];
} pkt_t;

int func(pkt_t *b)
{
    int *typep;
    char *ptr;

    /* #1: Generates warning */
    typep = &((cmd_t*)(&(b->data[0])))->type;

    /* #2: Doesn't generate warning */
    ptr = &b->data[0];
    typep = &((cmd_t*)ptr)->type;

    return *typep;
}

当我使用GCC编译时,我得到“解除引用类型 - 惩罚指针会破坏严格别名规则”警告。

  1. 为什么我会收到这个警告?我在char数组中取消引用。将char *投射到任何内容都是合法的。这是一个数组与指针不完全相同的情况吗?

  2. 为什么两个作业都不会产生警告?第二个任务相当于第一个,不是吗?

1 个答案:

答案 0 :(得分:0)

当打开严格别名时,允许编译器假设两个不同类型的指针(在此实例中为char* vs cmt_t*)不会指向相同的内存位置。这允许更大范围的优化,如果它们确实指向相同的内存位置,则不希望应用这些优化。可以在question中找到各种示例/恐怖故事。

这就是为什么,在严格别名下,你必须小心你如何打字。我相信这个标准不允许任何类型的惩罚(不要引用我),但大多数编译器都对工会有豁免(我的google-fu无法提出相关手册页):

union float_to_int {
    double d;
    uint64_t i;
};

union float_to_int ftoi;
ftoi.d = 1.0;
... = ftoi.i;

不幸的是,这对你的情况并不适用,因为你必须将memcpy数组内容放入联合中,这不太理想。更简单的方法是简单地通过-fno-strict-aliasing开关关闭严格别名。这将确保您的代码是正确的,并且不太可能产生重大的性能影响(如果性能很重要,请进行衡量)。

至于为什么当线路被打破时警告没有出现,我不知道。有可能对源代码的修改设法混淆了编译器的静态分析通过,以至于它没有看到类型惩罚。请注意,负责检测类型 - 双关语的静态分析通道是不相关的,并且不会与假定严格别名的各种优化通道进行对话。您可以将编译器完成的任何静态分析(除非另有说明)视为尽力而为的类型。换句话说,没有警告并不意味着没有错误,这意味着简单地分解线并不会神奇地使你的打字安全。