我在尝试翻转号码的所有位时遇到了一个奇怪的问题。
#include <cstdint>
constexpr uint16_t DefaultValueForPortStatus { 0xFFFF };
void f(uint16_t x)
{
}
int main()
{
f(~(DefaultValueForPortStatus));
}
Run Code Online (Sandbox Code Playgroud)
当我编译这个程序(GCC trunk)时,我收到一个错误:
警告:从“int”到“uint16_t”的无符号转换{aka“short unsigned int”}将值从“-65536”更改为“0”[-Woverflow]
当我从类型说明符中删除 constexpr 时,不会出现警告。这是为什么?为什么编译器将 uint16_t constexpr 变量更改为 int,而在非 constexpr 的情况下一切都很好?
Qui*_*mby 14
恕我直言,这是由关于整数提升的一个非常不幸的 C++ 规则引起的。它基本上指出,如果可以表示原始类型的所有值,则所有小于的类型int总是被提升为。仅当没有时才选择。在标准 32/64 位架构上属于第一类。intintunsigned intstd::uint16_t
int保证至少为 16 位宽,如果碰巧是这种情况,unsigned int则会被选择,因此代码的行为是实现定义的。
我不确切地知道为什么编译器仅针对constexpr值发出警告,很可能是因为它可以轻松地通过~. 在其他情况下,有人可能会更改为某些“安全”值,该值在求反并从backDefaultValueForPortStatus转换为 时不会溢出。但无论常量如何,问题都存在,您可以使用以下代码进行测试:intstd::uint16_t
#include <type_traits>
#include <cstdint>
constexpr uint16_t DefaultValueForPortStatus { 0xFFFF };
int main()
{
auto x = ~(DefaultValueForPortStatus);
static_assert(std::is_same_v<decltype(x), int>);
}
Run Code Online (Sandbox Code Playgroud)
相关标准部分:
由于整数提升,在 32 位平台上int,表达式
~(DefaultValueForPortStatus)
计算结果为 an int,值为-65536。
具体发生的情况如下:
在进行按位非 ( ~) 运算之前,操作数DefaultValueForPortStatus会提升为 an int,因此其内存表示形式将等同于具有unsigned int以下值的 an 的表示形式:
0x0000FFFF
在对其应用按位非 ( ~) 运算符后,其内存表示将等同于具有unsigned int以下值的 an 的内存表示:
0xFFFF0000
但是,结果的数据类型为int,而不是unsigned int。(我仅使用 的等效值unsigned int来说明内存表示形式。)因此,结果的实际值为-65536(因为 C++ 要求对有符号整数使用二进制补码内存表示形式)。
当将此转换int为uint16_t匹配函数参数的类型时,将丢弃 16 个最高有效位并保留 16 个最低有效位。因此,函数参数的值为0。
使用默认设置进行编译时,如果此类截断出现在常量表达式中,则 gcc 会发出警告。可以使用命令行选项禁用此警告-Wno-overflow。
编译器默认发出警告的原因可能是因为它假设此类截断不会出现在常量表达式中。它不会对基于非const变量的表达式做出此假设。
无论编译器是否发出警告,截断都会发生。
| 归档时间: |
|
| 查看次数: |
1112 次 |
| 最近记录: |