gcc 没有针对严格的别名规则发出警告

gcc not warning against the Strict Aliasing Rules

这几天我一直在阅读一些关于严格别名规则的文章。以下是我的理解:

  1. 对象的有效类型是其声明的类型。如果该对象是分配的内存,则在被具有有效类型的左值访问之前它没有内存,该左值成为该对象的有效类型。

  2. 对对象值的访问必须通过与其有效类型兼容的类型。

在我认为我得到这个之后,我想做一个简单的实验,看看我的编译器是否真的在我故意违反规则时发出警告。这是我的代码:

int main(void) {
    unsigned char c[5] = {0x1, 0x2, 0x3, 0x4, 0x5};
    int i = *(int*)c;
    printf("%x\n", i);
    
    return 0;
}

对我来说这似乎是违反规则的,因为 c 有一个有效类型 char 而我们正在尝试使用 int 指针访问它。

但我的 gcc 编译得很好!即使具有最高约束级别 (-Wstrict-aliasing),它也不会给出一个警告。但奇怪的是,用 float 替换 int 给出了预期的响应:

int main(void) {
    unsigned char c[5] = {0x1, 0x2, 0x3, 0x4, 0x5};
    float i = *(float*)c;
    printf("%f\n", i);
    
    return 0;
}

编译器对此代码给出警告。 (dereferencing type-punned pointer will breakk strict-aliasing rules [Wstrict-aliasing])

第一个代码真的违规了吗?我知道将任何指针转换为 char* 类型都可以,但反过来是这样吗?或者它只是 gcc 不太关心的东西?

根据已发布的 C 标准基本原理,称为“严格别名规则”的约束的目的是避免要求编译器给出如下内容:

int x;
int test(double *p)
{
  x = 1;
  *p = 2.0;
  return x;
}

考虑到存储到 *p 的可能性可能会影响 x 的值,即使代码中没有任何内容表明可能会发生这种事情。由于该标准明确规定,实现可以在“以环境特征的文档化方式”强加任何要求的情况下处理构造,因此作者认为没有必要完全列举他们期望实现一致处理的所有构造。因此,为了避免使语言变得无用,每个编译器都必须有意义地处理一些违反所写“别名”约束的构造

据我所知,gcc 执行此操作的方法之一是在某些超出约束规定的情况下双向应用规则。所写的规则不要求实现允许使用 int 类型的左值访问字符数组类型的对象的可能性,但它们也不要求实现允许这样的可能性包含整数数组的结构对象可以通过取消引用 int* 来访问,例如在 myStruct.intArray[2] 等表达式中由数组衰减形成的对象。如果 gcc 的作者认识到将构造视为违反“别名”约束是愚蠢的,他们会将构造视为没有违反约束,因此不会发出警告。