【问题标题】:C++ - Bit-wise not of uchar produces intC++ - uchar 按位不产生 int
【发布时间】:2014-09-29 13:37:13
【问题描述】:

我对 C++ 在按位应用而不是无符号字符时的行为感到惊讶。

取二进制值01010101b,即0x55,或85。对 8 位表示不按位应用应该会产生 10101010b,即 0xAA170

但是,我无法在 C++ 中重现上述内容。以下简单断言失败。

assert(static_cast<unsigned char>(0xAAu) == ~static_cast<unsigned char>(0x55u));

我使用以下代码打印了0x550xAA~0x55(作为uchar)的值。它揭示了按位不做我期望它做的事情。

std::cout << "--> 0x55: " << 0x55u << ", 0xAA: " << 0xAAu << ", ~0x55: "
     << static_cast<unsigned>(~static_cast<unsigned char>(0x55u)) << std::endl;

--> 0x55: 85, 0xAA: 170, ~0x55: 4294967210

~0x55 打印的数字等于11111111111111111111111110101010b,这不是0x55 的32 位位。因此,~ 运算符对 32 位整数进行操作,即使我将输入显式转换为 unsigned char。这是为什么呢?

我应用了另一个测试来查看~ 运算符返回的类型。结果在unsigned char 输入上是int

template <class T>
struct Print;

// inside main()    
Print<decltype(~static_cast<unsigned char>(0x55))> dummy;

产生以下编译器错误,这表明结果的类型为 int

error: implicit instantiation of undefined template 'Print<int>'
    Print<decltype(~static_cast<unsigned char>(0x55u))> dummy;

我做错了什么?或者,如何让 C++ 从 ~0x55 生成 0xAA

完整代码是here

【问题讨论】:

    标签: c++ bit-manipulation integer-promotion


    【解决方案1】:

    积分提升是在 ~ 的操作数上执行的,我们可以通过转到 draft C++ standard 部分 5.3.1 看到这一点 一元运算符,上面写着(强调我的 ):

    ~ 的操作数应具有整数或无范围枚举类型;这 result 是其操作数的反码。 综合促销 被执行。结果的类型是提升的操作数的类型[...]

    并且积分促销在4.5部分中进行了介绍积分促销并说:

    除 bool、char16_t、char32_t 或 wchar_t 其整数转换等级 (4.13) 小于 如果 int 可以表示所有,则 int 可以转换为 int 类型的纯右值 源类型的值;

    为了完整性,要查看 unsigned char 等级小于 int 的等级,我们可以转到 4.13 部分 整数转换等级 上面写着:

    有符号整数类型的秩应大于 任何尺寸较小的有符号整数类型。

    和:

    char 的秩应该等于signed char 和unsigned 的秩 字符。

    一种解决方案是将结果分配给一个 unsigned char,这是安全的,因为您不必担心有符号整数溢出。

    正如 Ben Voigt 指出的那样,拥有一个包含 sizeof (int) == 1CHAR_BIT &gt;= 32 的系统是合规的。在这种情况下,unsigned char 的等级不会小于 int,因此提升将是 unsigned int。我们不知道实际发生这种情况的任何系统。

    【讨论】:

    • +1:虽然很明显,但值得一提的是,unsigned char 确实 (a) 转换排名低于 int,并且 (b) 完全可以表示为 int(所有unsigned char 值可以表示为 int)。因此,促销是合理的。
    • 感谢您的详细解答!
    • 我进一步研究了它。其他二元运算&amp;| 等也发生了同样的事情。因此,这个故事的寓意似乎是应该将表达式中的值至少视为整数。
    • @Lemming 通常 C 和 C++ 使用 int 和更大的数据类型进行数学和按位运算,请参阅 Why must a short be converted to an int before arithmetic operations in C and C++?
    • 谢谢! IIRC int 的确切大小没有严格定义。这意味着即使是断言~0x55555555u==0xAAAAAAAAu 实际上也是不明确的,对吗?因为int 可能是 64 位长,在这种情况下,As 之前会有一堆 Fs。
    【解决方案2】:

    关于积分提升的答案是正确的。

    您可以通过以正确的顺序进行强制转换和 NOTting 来获得所需的结果:

    assert(static_cast<unsigned char>(0xAAu) == static_cast<unsigned char>(~0x55u));
    

    【讨论】:

      【解决方案3】:

      您可以通过将 ~0x55 的结果分配给 unsigned char 来“截断”前导 1:

      #include <iostream>
      
      int main()
      {
          unsigned char input = 0x55;
          unsigned char output = ~input;
      
          std::cout << "input: " << (int)input << " output: " << (int)output << std::endl;
      
          return 0;
      }
      

      结果:

      input: 85 output: 170
      

      【讨论】:

      • 不需要进行| 操作,只需分配给unsigned char 变量即可:output = ~input
      • @MarkRansom 太真实了!谢谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-08
      • 1970-01-01
      相关资源
      最近更新 更多