最近遇到了一个编译告警,严格别名和类型双关,整理一下相关的材料。
strict aliasing
以下问题摘自1
I have the following questions:
- If I leave the code with this warning, will it generate potentially wrong code?
- Is there any way to work around this problem?
- If there isn’t, is it possible to turn off strict aliasing from inside the source file (because I don’t want to turn it off for all source files and I don’t want to make a separate Makefile rule for this source file)?
And yes, I actually need this kind of aliasing.
The strict aliasing rule makes this setup illegal, two unrelated types can’t point to the same memory. char * are defined as being able to alias anything, only char* / unsigned char* has this privilege
.
aliasing assumptions
这个告警只有在 strict aliasing optimization
开启的情况下才会出现,gcc
在 -O2
优化会开启上述优化2,因此会出现该告警,忽略该告警有可能产生错误代码。出现该告警的原因是编译器为了优化代码,做了一个假设,即指针不会有其他类型的别名, (char *)
例外,意思是两个类型不同的指针不会指向同一块内存。当然这个特性孰好孰坏我们这里暂不讨论,实际上争议还是比较大的3。
Aliasing rules simply say that you can only access an object through its own type, its signed / unsigned variant type, or through a character type (signed char, unsigned char).
如何解决
如题主所问,如果我们确实需要做指针强制类型转换,怎样才能解决这个告警并得到健壮的代码呢?
C/C++
是非常贴近硬件底层的语言,指针为我们编程提供了很大的便利性,同时也引入了一些隐患。我们可以很方便地重新解析一块内存上的数据(reinterpret
) ,隐患是相伴便利性而生的,有时指针类型强转并不安全,例如出现未初始化字节数据的问题。
我们可以采用以下做法来消除这个告警:
- 采用
__attribute__((__may_alias__))
属性去提示编译器对某个变量关闭上面提到的假设。 - 关闭这个假设,
-fno-strict-aliasing
,可能影响优化,linux kernel
有大量的指针操作,其构建带这个选项。 - 屏蔽这个告警,
-Wno-strict-aliasing
,很不推荐!有掩耳盗铃之嫌! - 使用
union
来解决,这也是gcc
推荐的做法。type punning via unions feels natural
。
int retLen;
someSetFunc((unsigned long*)&retLen);
printf("ret len = %d\n", retLen);
...
union u_retLen {
int retLen;
unsigned long ptr;
};
someSetFunc(&u_retLen.ptr);
printf("ret len = %d\n", u_retLen.retLen);