g++-8 和早期版本之间的奇怪行为
Strange behavior between g++-8 and earlier versions
最近,在将我们的应用程序从 gcc-5.3 移植到 8.2 时,我们注意到一种奇怪的行为破坏了我们的应用程序。
简而言之,似乎 gcc-8.2 删除了我们的"比较 2 个无符号整数的 if 分支"之一,甚至没有产生警告。
我们使用相同的编译选项尝试了 g++ 5.3、g++ 7.4 和 g++ 8.2,只有 g++ 8.2 有这个问题。将在下面显示一个简短的示例。
#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;
}
上面的代码编译如下:
$ 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 仍然处理不需要的结果:
#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;
}
输出:
$ 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
问题(除了原始示例中的联合双关语(是这个表达式:
myunion2(a).lo() * myunion2(b).hi();
操作数的值为 65535 * 65535。操作数的类型uint16_t
。
算术运算不对小于 int
的类型执行。首先提升较小的类型。由于uint16_t
小于int
,而uint16_t
表示的值的范围可以用int
表示,这些操作数被提升为int
。但操作 65535*65535 溢出int
,属于有符号类型。签名溢出具有未定义的行为。
解决方案:在乘法之前转换为较大的无符号(或首先返回较大的无符号(:
const uint32_t x1 = (unsigned)myunion2(a).hi() * myunion2(b).lo();
const uint32_t x2 = x1 + (unsigned)myunion2(a).lo() * myunion2(b).hi();
相关文章:
- 特征版本 3.2.0 和 3.3.4 之间的数值差异
- 为什么库API+编译器ABI足以确保具有不同版本gcc的对象之间的兼容性
- 检测到 Conan 配置文件设置与 CMake 之间的编译器版本不匹配
- 为什么C++中的表达式类型在不同版本之间会发生变化
- 为什么CRC计算在C和C 版本之间有所不同
- 两个提升版本之间的冲突
- .vcxproj 和 .vcxproj.filters 中的工具版本之间的差异
- boost::v1.59 和 v1.60 之间的序列化存档版本
- g++-8 和早期版本之间的奇怪行为
- 如何在两个版本之间从perforce差异获取C/C 函数名称
- 具有C 接口的C 动态库如何在不同的编译器版本之间不会破坏ABI
- GLIBCXX(libstdc++.so.6)与gcc版本之间的关系
- 游戏"Life"中发布和调试版本之间的加速比例奇怪
- 最新版本的提升和提升几何库 (GGL) 之间的冲突
- 是否可以有一个零成本assert(),这样代码就不必在调试和发布版本之间进行修改
- 如何在不同的MS Office版本之间进行C 应用程序切换
- 如何在本机C++(Windows 7及以前版本)和通用C++(Windows 8及以后版本)应用程序之间共享代码
- 版本之间定义的实现
- C++版本之间的差异
- 调试版本和发布版本之间可能出现的差异