【问题标题】:Why does C++ prints unsigned char value as negative?为什么 C++ 将 unsigned char 值打印为负数?
【发布时间】:2018-06-30 13:34:07
【问题描述】:

我试图理解 C++ 中的隐式转换规则,并且我理解当两个主要类型之间存在一个操作时,“较低类型”会提升为“较高类型”,所以让我们说:

int a = 5;
float b = 0.5;

std::cout << a + b << "\n";

应该打印 5.5,因为 'a' 被提升为浮点类型。我也明白无符号类型是比有符号计数器部分“更高的类型”,所以:

int c = 5;
unsigned int d = 10;

std::cout << c - d << "\n";

打印 4294967291 因为 'c' 被提升为无符号整数,并且由于无符号类型在小于零时回绕,所以我们得到了那个大数字。

但是对于以下情况,我不明白为什么我得到 -105 而不是正数。

#include <iostream>

int main(void) {
    unsigned char a = 150;
    std::cout << static_cast<int>(a - static_cast<unsigned char>(255)) << "\n";
    return 0;
}

我猜是这段代码:

a - static_cast<unsigned char>(255)

应该产生一个正数,所以最终的强制转换(到 int)不应该影响最终结果,对吧?

【问题讨论】:

  • 您忘记了整数提升(所有小于 int 的整数类型,包括 char,在我们到达您在开始时描述的规则之前都被提升为 int发布)
  • 这是 C++。您应该删除 C 标记。
  • 你猜a - static_cast&lt;unsigned char&gt;(255) 是肯定的,但从不费心去测试它。你应该有。
  • @Mark Benningfield 已删除。

标签: c++ char unsigned type-promotion


【解决方案1】:

您缺少从 unsigned charint 的(隐式)转换,该转换恰好执行 -(减)操作。每当您尝试将任何整数运算应用于小于 int 的某个整数类型的值时,都会发生这种整数提升。

【讨论】:

  • “到某个小于 int 的整数类型的值”很好,不包括 boolchar16_tchar32_twchar_t,不是吗?
  • @SouravGhosh:不,特定类型没有例外。当然,其中一些类型在某些平台上可能不会小于int,因此在这些平台上,不会应用任何转换。
  • 谢谢!我不知道这种隐式转换!顺便说一句“......将任何整数运算应用于小于 int 的某个整数类型的值。”所以这意味着它也适用于乘法 (*) 和除法 (/) 运算符或仅适用于和 (+) 和减法 (-)?我试过乘法(*),似乎没有发生隐式转换。
  • @SouravGhosh 以扩展 Chris 的评论,这些类型的促销由同一部分中的其他规则指定,您将排除这些类型的规则复制到您的答案中。
  • @user2079303 对,只是不限制它们为intunsigned int。无论如何,克里斯在 cmets 中所说的都是真的。
【解决方案2】:

引自C++14,第 5.7 章

加法运算符 +- 从左到右分组。通常的算术转换是为 算术或枚举类型的操作数。

对于通常的算术转换,(针对这种情况)

....

  • 否则,应在两个操作数上执行积分提升 (4.5)

最后,对于整体促销,第 § 4.5 章

boolchar16_tchar32_twchar_t 之外的整数类型的纯右值,其整数转换 rank (4.13) 小于 int 的 rank 如果 int 可以代表所有,则可以转换为int 类型的prvalue 源类型的值;否则,源纯右值可以转换为unsigned int.类型的纯右值

因此,unsigned char 操作数被提升为int,然后计算结果。

【讨论】:

  • 这条规则背后的想法,我觉得记住它非常有用,它是小于int 的整数类型主要被视为“存储类型”,即作为一种节省空间的方式在记忆中。当它们被读回寄存器以对其执行操作时,它们自然会提升到机器寄存器大小(通常与int一致)并相应地执行操作。
  • @MatteoItalia 是的,确实。
  • 非常感谢您的回答。我用 char16_t 再次尝试了我的代码,但它也导致了一个负数,这种类型不应该是这种隐式转换的例外吗?
  • @RandomGuy 好吧,不。这些类型的范围更广,但它们也会得到提升。
【解决方案3】:

这里有显示正在发生的事情的答案。我不会重复。我会给你一个简单的工具来帮助你。

您可以使用以下技巧快速找到表达式的类型:

template <class> struct Name; // purposely no definition given

Name<decltype(your_expression)> n;

这将为未定义的模板“名称”生成编译器错误,但我们真正感兴趣的是将出现在错误消息中的模板参数的类型。

例如如果您想查看在两个unsigned char 之间进行算术运算时会得到什么类型:

#include <utility>
template <class> struct Name;

auto test()
{
    Name<decltype(std::declval<unsigned char>() - std::declval<unsigned char>())> n;

   // or
   unsigned char a{};
   Name<decltype(a - a)> n2;
}

会得到你

error: implicit instantiation of undefined template 'Name<int>'

这会告诉你表达式的类型是int

当然这不会告诉您所涉及的规则,但它是查看表达式类型或验证您对表达式类型的假设的快速起点。

【讨论】:

  • 谢谢!这有帮助!在 Visual Studio 2013 编译器和 gcc (mingw) 中运行良好。
猜你喜欢
  • 1970-01-01
  • 2012-05-14
  • 1970-01-01
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
  • 2012-07-07
  • 2013-01-19
  • 2017-05-07
相关资源
最近更新 更多