首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >g++-8和早期版本之间的奇怪行为

g++-8和早期版本之间的奇怪行为
EN

Stack Overflow用户
提问于 2019-03-27 14:27:53
回答 1查看 131关注 0票数 5

最近,当我们的应用程序从gcc-5.3移植到8.2时,我们注意到一种奇怪的行为破坏了我们的应用程序。

简而言之,gcc 8.2似乎删除了我们的一个"if分支,比较两个无符号整数“,甚至没有产生警告。

我们使用相同的编译选项尝试了g++ 5.3、g++ 7.4和g++ 8.2,只有g++ 8.2有这个问题。将在下面显示一个简短的例子。

代码语言:javascript
复制
#include <iostream>
#include <cstdint>
#include <cstdlib>
#include <cstring>

using namespace std;

struct myunion {
    myunion(uint32_t x) {
        _data.u32 = x;
    }
    uint16_t hi() const { return _data.u16[1]; }
    uint16_t lo() const { return _data.u16[0]; }
    union {
        uint16_t u16[2];
        uint32_t u32;
    } _data;
};

 __attribute__((noinline)) void printx1x2(uint32_t x1, uint32_t x2) {
    cout << "x1: " << x1 << endl;
    cout << "x2: " << x2 << endl;
}

__attribute__((noinline)) int func(uint32_t a, uint32_t b) {
    const uint32_t x1 = myunion(a).hi() * myunion(b).lo();
    const uint32_t x2 = x1 + myunion(a).lo() * myunion(b).hi();
    printx1x2(x1, x2);
    int ret = 0;
    if ( x2 < x1 ) {
        ret = 0x10000;
    }
    return ret;
}

int main(int argc, char** argv) {
    cout << func(4294967295, 4294917296) << endl;
    return 0;
}

上述代码编译如下:

代码语言:javascript
复制
$ g++-7 --version
g++-7 (GCC) 7.4.1 20181207
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++-7 -Wall -std=c++14 -O3 a.cxx -o 7.out
$ ./7.out
x1: 1018151760
x2: 1018020689
65536

$ g++ --version
g++ (GCC) 8.2.1 20181127
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ -Wall -std=c++14 -O3 a.cxx -o 8.out
$ ./8.out
x1: 1018151760
x2: 1018020689
0

我期望7.out的输出是正确的。

这实际上是UB (未定义的行为),还是可以是g++错误?

更新

看起来,删除工会访问UB仍然处理不想要的结果:

代码语言:javascript
复制
#include <iostream>
#include <cstdint>
#include <cstdlib>
#include <cstring>

using namespace std;

struct myunion2 {
    myunion2(uint32_t x) {
        _data = x;
    }
    uint16_t hi() const { return (uint16_t)((_data & 0xFFFF0000) >> 16); }
    uint16_t lo() const { return (uint16_t)((_data & 0xFFFF)); }
    uint32_t _data;
};

 __attribute__((noinline)) void printx1x2(uint32_t x1, uint32_t x2) {
    cout << "x1: " << x1 << endl;
    cout << "x2: " << x2 << endl;
}

__attribute__((noinline)) int func(uint32_t a, uint32_t b) {
    const uint32_t x1 = myunion2(a).hi() * myunion2(b).lo();
    const uint32_t x2 = x1 + myunion2(a).lo() * myunion2(b).hi();
    printx1x2(x1, x2);
    int ret = 0;
    if ( x2 < x1 ) {
        ret = 0x10000;
    }
    return ret;
}

int main(int argc, char** argv) {
    cout << func(4294967295, 4294917296) << endl;
    return 0;
}

输出:

代码语言:javascript
复制
$ g++-7 -Wall -std=c++14 -O3 a.cxx -o 7.out
[2019-03-27 22:48:30][wliu@wliu-arch-vm1 ~/tests]
$ ./7.out
x1: 1018151760
x2: 1018020689
65536
[2019-03-27 22:48:32][wliu@wliu-arch-vm1 ~/tests]
$ g++ -Wall -std=c++14 -O3 a.cxx -o 8.out
[2019-03-27 22:49:11][wliu@wliu-arch-vm1 ~/tests]
$ ./8.out
x1: 1018151760
x2: 1018020689
0
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-27 15:13:56

问题(除了原始示例中的联合双关语)是这样的:

代码语言:javascript
复制
myunion2(a).lo() * myunion2(b).hi();

操作数的值为65535 * 65535。操作数的类型是uint16_t

算术操作不对小于int的类型执行。首先提升较小的类型。由于uint16_tint小,并且uint16_t表示的值范围可以用int表示,所以这些操作数被提升到int。但是操作65535 * 65535溢出int,这是一个有符号的类型。签名溢出有未定义的行为。

解决方案:在乘之前转换为更大的无符号(或首先返回更大的无符号):

代码语言:javascript
复制
const uint32_t x1 = (unsigned)myunion2(a).hi() * myunion2(b).lo();
const uint32_t x2 = x1 + (unsigned)myunion2(a).lo() * myunion2(b).hi();
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55379684

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档