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

摘要:
最近将手头上负责的项目代码从GCC4.8.2升级到了GCC8.2。上述代码在GCC4.8.2之中并不会出现问题,但是一旦切换到GCC8.2之后,并且在编译优化等级大于1的时候,就会core在这个函数的执行代码位置。GCC8.2也囊括了绝大多数C++17的新特性和部分的C++2a的特性,各种新的语法糖在编码过程之中也能极大的提高开发效率。

最近将手头上负责的项目代码从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。

免责声明:文章转载自《C++雾中风景番外篇4:GCC升级二三事》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇判断是否为空….IsEmpty(Power Query 之 M 语言)十个PHP开发者最容易犯的错误下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C语言使用正则表达式

http://blog.chinaunix.net/uid-479984-id-2114941.html C语言使用正则表达式 据说一个好的程序员是会使用DB和Regular Expression的程序员,可见两者是多么重要。正则表达式是能极大地提高工作效率的工具,使用过Linux下各种具备RE特性的工具的人一定对此深 有感触。很多语言都支持RE,用的最多...

[蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)

开机初始化Log Log编号                       函数名                             所在文件名 1 000001: main...

浅谈C工程中的.c与.h文件

基于C语言的单片机、arm相关的工程开发时,C语言的模块化特点体现的非常明显。试想一下:你的一个工程中需要用到AD采样模块、液晶显示模块、串口发送模块、DA控制模块等。你肯定不会选择在一个.c文件中进行,必须是分模块的,这样才有利于团队开发,提高效率。 那么模块化设计遵循着怎样的原则呢,应该怎么写.c,.h文件呢。 1. .c和.h文件的区别 通常意义上的...

【VS开发】【C/C++开发】memcpy和memmove的区别

memcpy和memmove()都是C语言中的库函数,在头文件string.h中,作用是拷贝一定长度的内存的内容,原型分别如下: void *memcpy(void *dst, const void *src, size_t count); void *memmove(void *dst, const void *src, size_t count);...

十六进制字符串 char 数组 转换 c/c++/java

转载自:http://qing.blog.sina.com.cn/1820422183/6c81702733001qvk.html 1.c版 int hexcharToInt(char c) { if (c >= '0' && c <= '9') return (c - '0'); if (c >= 'A' &&...

Crypto++入门学习笔记(DES、AES、RSA、SHA-256)

最先附上下载地址 背景(只是个人感想,技术上不对后面的内容构成知识性障碍,可以skip): 最近,基于某些原因和需要,笔者需要去了解一下Crypto++库,然后对一些数据进行一些加密解密的操作。 笔者之前没接触过任何加密解密方面的知识(当然,把每个字符的ASCII值加1之流对明文进行加密的“趣事”还是干过的,当时还很乐在其中。),甚至一开始连Crypto+...