zoukankan      html  css  js  c++  java
  • C++雾中风景番外篇4:GCC升级二三事

    最近将手头上负责的项目代码从GCC 4.8.2升级到了GCC 8.2。(终于可以使用C++17了,想想后续的开发也是很美好啊~~)不过这个过程之中也遇到了一些稀奇古怪的问题,在这里做一个简单的记录,希望后续有同学遇到类似的问题能作为参考。

    1. error: unable to find string literal operator 'operator"

    这个我感觉是历史的遗留问题了,从C++11开始就不支持字符串字面量后面直接连接变量名,GCC 4.8.2应该是没有支持该编译检查,所以后续升级8.2的时候报了类似的错误。

    听着有些抽象啊,举个栗子:

    #define LOG(fmt, ...) printf("[%s][%s][%d]:"fmt "
    ", __FILE__, __FUNCTION__,
                                __LINE__, ##__VA_ARGS__)
    

    上面是一段C++常用的日志宏定义,在宏定义展开的时候,编译器会默认将[%s][%s][%d]:,fmt," "字面量拼接在一起,然后和后面行号等宏定义作为参赛打印出来。

    #define LOG(fmt, ...) printf("[%s][%s][%d]:" fmt "
    ", __FILE__, __FUNCTION__,
                                __LINE__, ##__VA_ARGS__)
    

    2. error: flexible array member not at end of struct

    在C++之中,给定了一个结构定义和一个指向结构的指针,编译器必须能够通过指针偏移的方式访问该结构的任何成员。由于结构中每个成员的位置都取决于其前导成员的数量和类型,因此访问任何结构都需要知道所有前导成员的数量和类型。

    在结构体之中,如果是数组为结构体之中最后的成员。这并不违反上述的编译规则。但是,如果flexible array member出现在了结构体末尾以外的任何位置,则其后的任意成员的位置都将取决于数组中对应的类型的个数,所以编译器禁止将没有定义长度的数组作为结构体的中间成员。

    举个栗子:

    struct S {
        int a;
        char b[];
        int c;
    };
    

    这里由于b成员的长度是不确定的,所以编译器无法通过S的指针推断出成员c的位置,所以编译报错:
    b不在结构体S的末尾

    而我们看如下的结构体就没有编译报错的问题了:

    struct S {
        int a;
        int c;
        char d[];
    };
    

    看到这,可能有些读者会问了,如果我就是需要在结构体之中定义两个变长的数组,能怎么办呢? 笔者有觉得有下面两种方式实现:

    • 用指针啊!!!把结构定义为下面这种形式就可以了
    struct S {
        int a;
        char b[0];
        int c;
        char d[];
    };
    
    • 如果b和d成员长度一致或者不在内存损耗的情况,也可以采用如下方式来定义这个结构体:
    struct Pair {
        char b;
        char d;
    }
    struct S {
        int a;
        int c;
        Pair p[];
    };
    

    3. 返回值的坑

    有返回值的函数没有指定return,或是return了却没有给出返回值在gcc进行-O优化等级大于1时,会出现各种稀奇古怪的core。笔者也是通过GDB调试了很久,最终通过编译器的警告发现了上述的问题。

    这个理论上是一个很低级的错误,但是笔者花了比较长的时间排查,因为出现的实在是有些诡异。

    我们来看如下代码:

    int test(int a, int b) {
        auto c = a + b;
    }
    
    int main() {
        auto c = test(10, 20);
        return 0;
    }
    

    上面我们可以看到test函数本身是需要返回一个int类似作为返回值的,但是这里并没有进行应有的返回。

    上述代码在GCC 4.8.2之中并不会出现问题,但是一旦切换到GCC8.2之后,并且在编译优化等级大于1的时候,就会core在这个函数的执行代码位置。

    所以为了规避上述的问题,笔者这里推荐使用GCC编译时开启编译选项:-Werror=return-type。这样,有上述返回值问题的代码就会在编译期间被编译器识别并报错。(其实参数-W,-Wall编译器是会对上述问题报警的,但是warning嘛,大家经常就不care啊~~~)

    4.小结

    简单总结了一下笔者升级GCC过程之中遇到的一些小的编译问题,希望可以帮助到同样问题的同学。GCC8.2也囊括了绝大多数C++17的新特性和部分的C++2a的特性,各种新的语法糖在编码过程之中也能极大的提高开发效率。Enjoy your modern CPP。

  • 相关阅读:
    Codeforces Round #439 (Div. 2) B. The Eternal Immortality
    Codeforces Round #439 (Div. 2) A. The Artful Expedient
    Codeforces Round #437 (Div. 2, based on MemSQL Start[c]UP 3.0
    ClassLoader
    UVA 10790 How Many Points of Intersection?
    HDU 4628 Pieces
    Java学习笔记——可视化Swing中JTable控件绑定SQL数据源的两种方法
    thrift之TTransport层的分帧传输类TFramedTransport
    VB6基本数据库应用(四):数据的提取,新增和修改
    android 开发中判断网络是否连接的代码
  • 原文地址:https://www.cnblogs.com/happenlee/p/12306936.html
Copyright © 2011-2022 走看看