C/C++ strict-aliasing

最近发现了一个奇怪的编译参数-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

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月15日 上午12:38
下一篇 2023年2月15日 上午12:38

相关推荐