C++雾中风景番外篇4:GCC升级二三事

2020-02-24 12:36:43 浏览数 (1)

最近将手头上负责的项目代码从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的时候报了类似的错误。

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

代码语言:javascript复制
#define LOG(fmt, ...) printf("[%s][%s][%d]:"fmt "n", __FILE__, __FUNCTION__,
                            __LINE__, ##__VA_ARGS__)

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

代码语言:javascript复制
#define LOG(fmt, ...) printf("[%s][%s][%d]:" fmt "n", __FILE__, __FUNCTION__,
                            __LINE__, ##__VA_ARGS__)
2. error: flexible array member not at end of struct

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

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

举个栗子:

代码语言:javascript复制
struct S {
    int a;
    char b[];
    int c;
};

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

b不在结构体S的末尾

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

代码语言:javascript复制
struct S {
    int a;
    int c;
    char d[];
};

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

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

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

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

我们来看如下代码:

代码语言:javascript复制
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。

0 人点赞