违反严格别名并不总是会产生编译器警告

Violating strict aliasing does not always produce a compiler warning

本文关键字:警告 编译器 别名      更新时间:2023-10-16

严格的别名有点让我陷入循环。这是代码。

我有一堂课

#include <arpa/inet.h>
#include <net/route.h>
class Alias
{
public:
struct rtentry rt;
struct sockaddr_in *address;  
void try_aliasing()
{
address = (struct sockaddr_in *)&rt.rt_dst;
address->sin_family = AF_INET;
}
};


如果我像这样使用它:

int main()
{
Alias a ;
a.try_aliasing();
return 0;
}


它显示:

warning:dereferencing pointer '<anonymous>' does break strict-aliasing rules


但是,如果我将该类用作:

int main()
{
Alias *a = new Alias();
a->try_aliasing();
return 0;
}


它编译得很好。
两次编译都使用:

g++ a.cpp -Wall -O2


浏览了一些关于严格别名的线程,但他们未能为我清除这种行为的原因。

在大多数情况下,编译器能够生成"严格混叠"警告,它们同样可以使用不同的左值轻松识别存储上的操作之间的关系。 该标准不要求编译器使用不同类型的左值识别对存储的访问的原因是为了避免要求他们悲观地假设别名,否则他们没有理由期望它[因此没有理由发出任何警告]。

如前所述,该标准不承认非字符类型的左值可以从另一个左值派生并用于作为其自己的类型访问存储的任何情况。 甚至像这样:

struct foo {int x;} s = {0};
s.x = 1;

调用 UB,因为它使用int类型的左值来访问类型为struct foo的对象的存储。 标准的作者依靠编译器编写者来识别常识暗示他们应该可预测的行为而不考虑标准是否实际要求的情况。 我不认为任何编译器会愚蠢到不承认对s.x的操作实际上是对s的操作,并且在许多其他情况下,标准思想编译器的作者会有意义地识别这些事情而不被命令这样做。

不幸的是,一些编译器编写者已经开始将这些规则视为假设看起来像是使用另一种类型的左值访问一种类型的存储的代码不会这样做的理由,而不仅仅是对看起来不像的代码做出这样的假设。 给定如下内容:

void doSomehing(structs s2 *p);
void test(struct s1 *p)
{
doSomething((struct s2*)p};
}

有几种方法可以调用类似test的内容:

1. It might receive a pointer to a `struct s1`, which `doSomething` will need to operate upon [perhaps using the Common Initial Sequence guarantee] as though it is a `struct s2`.  Further, either:
1a. During the execution of `doSomething` storage accessed exclusively via pointers derived from a struct s2 like the one that was passed in will be accessed exclusively via such means, or...
1b. During the execution of `doSomething` storage accessed via things of that type will also be accessed via other unrelated means.
2. It might receive a pointer to a `struct s2`, which has been cast to a `struct s1*` for some reason in the calling code.
3. It might receive a pointer to a `struct s1`, which `doSomething` will process as though it's a `struct s1`, despite the fact that it accepts a parameter of type `struct s2`.

编译器可能会观察到标准定义的行为都不太可能,因此决定在此基础上发出警告。 另一方面,到目前为止最常见的情况是 #1a,编译器确实应该能够以可预测的方式处理它,无论标准是否要求,通过确保在函数中执行的对struct s2类型事物的任何操作在调用之前对类型struct s1的操作进行排序, 以及遵循函数调用的类型struct s1上的那些。 不幸的是,gcc和clang没有这样做。