c++ - 带符号和无符号操作数的按位 '&'

标签 c++ c++11 bitwise-and type-promotion sign-extension

我遇到了一个有趣的场景,根据正确的操作数类型,我得到了不同的结果,但我无法真正理解其中的原因。

这是最少的代码:

#include <iostream>
#include <cstdint>

int main()
{
    uint16_t check = 0x8123U;

    uint64_t new_check = (check & 0xFFFF) << 16;

    std::cout << std::hex << new_check << std::endl;

    new_check = (check & 0xFFFFU) << 16;

    std::cout << std::hex << new_check << std::endl;

    return 0;
}

我在 Linux 64 位上使用 g++(gcc 版本 4.5.2)编译了这段代码:g++ -std=c++0x -Wall example.cpp -o example

输出是:

ffffffff81230000

81230000

第一种情况下输出的原因我不太明白。

为什么在某些时候将任何时间计算结果提升为 signed 64bit 值 (int64_t) 从而导致符号扩展?

如果 16 位值首先向左移动 16 位然后提升为 64 位值,则在这两种情况下我都会接受结果为“0”。如果编译器首先将 check 提升为 uint64_t 然后执行其他操作,我也会接受第二个输出。

但是为什么 & 与 0xFFFF (int32_t) 与 0xFFFFU (uint32_t) 会导致这两个不同的输出?

最佳答案

这确实是一个有趣的极端案例。它只发生在这里,因为当您将 32 位用于 ìnt

架构时,您将 uint16_t 用于无符号类型

这是 C++14 草案 n4296 中第 5 条表达式的摘录(强调我的):

10 Many binary operators that expect operands of arithmetic or enumeration type cause conversions ... This pattern is called the usual arithmetic conversions, which are defined as follows:
...
(10.5.3) — Otherwise, if the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type shall be converted to the type of the operand with unsigned integer type.
(10.5.4) — Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, the operand with unsigned integer type shall be converted to the type of the operand with signed integer type.

你在 10.5.4 的情况下:

  • uint16_t 只有 16 位,而 int 是 32
  • int可以表示uint16_t
  • 的所有值

所以 uint16_t check = 0x8123U 操作数被转换为有符号 0x8123 并且按位 & 的结果仍然是 0x8123。

但是移位(按位,所以它发生在表示级别)导致结果是中间无符号 0x81230000 转换为 int 给出负值(技术上它是实现定义的,但这种转换是一种常见用法)

5.8 Shift operators [expr.shift]
...
Otherwise, if E1 has a signed type and non-negative value, and E1×2E2 is representable in the corresponding unsigned type of the result type, then that value, converted to the result type, is the resulting value;...

4.7 Integral conversions [conv.integral]
...
3 If the destination type is signed, the value is unchanged if it can be represented in the destination type; otherwise, the value is implementation-defined.

(注意这是 C++11 中真正的未定义行为...)

因此,您以将带符号的 int 0x81230000 转换为 uint64_t 结束,正如预期的那样给出 0xFFFFFFFF81230000,因为

4.7 Integral conversions [conv.integral]
...
2 If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type).

TL/DR:这里没有未定义的行为,导致结果的原因是将有符号的 32 位 int 转换为无符号的 64 位 int。 未定义行为的唯一部分是会导致符号溢出的转换,但所有常见的实现都共享这个,它是在 C++14 标准中定义的实现

当然,如果你强制第二个操作数是无符号的,那么一切都是无符号的,你显然会得到正确的 0x81230000 结果。

[编辑] 正如 MSalters 所解释的,转变的结果只是 自 C++14 以来定义的实现,但在 C++11 中确实是未定义的行为 .移位运算符段落说:

...
Otherwise, if E1 has a signed type and non-negative value, and E1×2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

关于c++ - 带符号和无符号操作数的按位 '&',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38737024/

相关文章:

c++ - 如何在 Linux 上注册/捕获终端内鼠标单击的位置?

c++ - 预定义宏和 C++11

c++ - 如何在 C++ 中混合原子和非原子操作?

c++ - 在这种情况下,while 循环是如何工作的?

python - 为什么下面代码的输出是这样的

c - 按位与运算不清楚

C++菜单避免重复

c++ - 在没有类型注册或 RTTI 的情况下在 C++ 中实现 type_id(T)

c++ - GDI+ 在为 vista 重绘时没有清除我的窗口

c++ - 在 C++ 中比较 & 与 % 的速度