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 "\n", __FILE__, __FUNCTION__,\
__LINE__, ##__VA_ARGS__)
上面是一段C++常用的日志宏定义,在宏定义展开的时候,编译器会默认将[%s][%s][%d]:
,fmt
,"\n"
字面量拼接在一起,然后和后面行号等宏定义作为参赛打印出来。
#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出现在了结构体末尾以外的任何位置,则其后的任意成员的位置都将取决于数组中对应的类型的个数,所以编译器禁止将没有定义长度的数组作为结构体的中间成员。
举个栗子:
struct S {
int a;
char b[];
int c;
};
这里由于b成员的长度是不确定的,所以编译器无法通过S的指针推断出成员c的位置,所以编译报错:
而我们看如下的结构体就没有编译报错的问题了:
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。