• 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
    ", 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派生类的类型
  • 相关阅读:
    Vue的响应式
    让html上两个元素在一行显示
    linux的<<命令
    http-only,withCredentials
    axios跨域请求时 withCredentials:true 表示request携带cookie
    异步代码async await阻塞进程的误区——await的是Promise的resolve而不是语句块的执行结束
    理解状态机
    关于express返回值的问题
    axios基本的get/post请求使用方法
    【转】 前端笔记之Vue(四)UI组件库&amp;Vuex&amp;虚拟服务器初识
  • 原文地址:https://www.cnblogs.com/ZisZ/p/9105383.html
Copyright © 2020-2023  润新知