首页 > 解决方案 > gcc 没有警告严格别名规则

问题描述

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

  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),它也不会发出任何警告。但奇怪的是,将 a 替换为intafloat给出了预期的响应:

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 不太关心的东西?

标签: cgcclanguage-lawyerstrict-aliasing

解决方案


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

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

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

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


推荐阅读