最近发现了一个奇怪的编译参数-fno-strict-aliasing,好奇之下做了一点研究;
重点参考Understanding C/C++ Strict Aliasing;
所谓的aliasing就是多个变量指向同一块内存,变量之间互为别名;
strict-aliasing是一种编译器希望开发者遵守的规则:虽然C/C++变量可以随便赋值(强制类型转换),但也请你们收敛一点,别太天马行空了;
如果开发者按照这个规则写代码了,编译器就可以做更好的代码优化,比如这个例子:
void foo(double *dblptr)
{
anint = 1;
*dblptr = 0;
bar(anint);
}
如果开发者能够注意不要把int转成double,bar(anint)可以直接优化成bar(1);
但没有任何约束不允许这样做,因而编译器不敢做这样的优化,只能在bar(anint)将anint传入bar之前加一条汇编指令再读一下anint的值;
如果开发者确定自己的代码遵守这样的规则了,可以在编译时加一个优化参数-fstrict-aliasing,这个参数在gcc的-O2、-O3、-Os优化级别下都是默认开启的。
然后我对Understanding C/C++ Strict Aliasing文中的两个主要例子做了一下测试:
例子一:
#include <stdio.h>
int anint;
void bar(int a)
{
printf("%d\n", a);
}
void foo(double *dblptr)
{
anint = 1;
*dblptr = 0;
bar(anint);
}
int main()
{
foo((double*)&anint);
return 0;
}
编译器版本 | 编译参数 | 结果 |
gcc 4.4.7 | g++ | 0 |
g++ -O3 | 1 | |
g++ -O3 -fno-strict-aliasing | 0 | |
gcc 4.8.5 | g++ | 0 |
g++ -O3 | 1 | |
g++ -O3 -fno-strict-aliasing | 0 | |
gcc 7.3.0 | g++ | 0 |
g++ -O3 | 1 | |
g++ -O3 -fno-strict-aliasing | 0 |
可以看到,这个case被gcc编译器优化坏了,可以用-fno-strict-aliasing规避;
例子二:
#include <iostream>
#include <iomanip>
using namespace std;
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
uint32_t swaphalves(uint32_t a) {
uint32_t acopy = a;
uint16_t *ptr = (uint16_t*)&acopy;// can't use static_cast<>, not legal.
// you should be warned by that.
uint16_t tmp = ptr[0];
ptr[0] = ptr[1];
ptr[1] = tmp;
return acopy;
}
int main() {
uint32_t a;
a = 32;
cout << hex << setfill('0') << setw(8) << a << endl;
a = swaphalves(a);
cout << setw(8) << a << endl;
}
编译器版本 | 编译参数 | 结果 |
gcc 4.4.7 | g++ | 00000020 00200000 |
g++ -O3 | 00000020 00000020 |
|
gcc 4.8.5 | g++ | 00000020 00200000 |
g++ -O3 | 00000020 00200000 |
|
gcc 7.3.0 | g++ | 00000020 00200000 |
g++ -O3 | 00000020 00200000 |
发现这个case有点意思,只在4.4版本的编译器上会出现问题,高版本编译器上已经修正了。
没有精力再深入研究,就到此为止。
最后再贴上strict aliasing的规则说明,下面这篇文章给出了较好的中文翻译,而且作者显然比我研究的更深入,我就直接抄过来了:
https://blog.csdn.net/dbzhang800/article/details/6720141
- 兼容类型(指相同类型?)或差别仅在于signed、unsigned、const、volatile的类型(比如 const unsigned long 和 long)
- 聚合类型(struct或class)或联合类型(union)可以alias它们所包含的类型(比如 int 和 包含有int的结构体(包括间接包含))
- 字符类型(char 、signed char、unsinged char*)可以 alias 任何类型的指针
- [C++] 基类的类型(可能带有const、volatile等cv修饰)可以alias派生类的类型
原文链接: https://www.cnblogs.com/ZisZ/p/9105383.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/274789
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!