【问题标题】:std::cout deal with uint8_t as a characterstd::cout 将 uint8_t 作为字符处理
【发布时间】:2016-08-25 12:47:15
【问题描述】:

如果我运行这段代码:

std::cout << static_cast<uint8_t>(65);

它会输出:

一个

这是数字 65 的 ASCII 等价物。 这是因为uint8_t 被简单定义为:

typedef unsigned char uint8_t;
  • 这种行为是标准吗?

  • 不应该是更好的方法来定义uint8_t,保证被处理为数字而不是字符?

我无法理解如果我想打印uint8_t 变量的值,它将被打印为字符的逻辑。

附:我正在使用 MSVS 2013。

【问题讨论】:

  • 是的,这是标准行为。如果您想使用 uint8_t 作为一个小的无符号整数,您需要在输出之前对其进行转换。就像例如static_cast&lt;uint32_t&gt;(some_uint8_t_variable)
  • 谢谢..它没有完全复制..我知道为什么会存在这种行为。我的主要两个问题是:这是否是一种标准行为以及为什么没有更好的方法将 uint8_t 表示为非字符类型。
  • @HumamHelfawi - 没有大小为 1 且不是 char 的变体的原始类型。是的,C++ 可以添加一个新类型,但这会与 C 冲突,而 C 正是这些 typedef 的起源。
  • std::cout &lt;&lt; +static_cast&lt;uint8_t&gt;(65); 会做你想做的事。
  • + 是一个一元运算符,其作用与一元 - 非常相似,只是它不否定值。它通常被视为毫无意义,但它在这里很有用,因为作为算术运算符,编译器将其参数提升为int。所以+x 等价于(int)x

标签: c++ c++11 language-lawyer cout fixed-size-types


【解决方案1】:

这种行为是标准吗

这种行为是标准的,如果uint8_tunsigned char 的typedef,那么它将始终打印一个字符,因为std::ostream 具有unsigned char 的重载,并将变量的内容打印为一个字符。

不应该是更好的方式来定义uint8_t,保证被处理为一个数字而不是一个字符?

为了做到这一点,C++ 委员会必须引入一种新的基本类型。目前,sizeof() 等于 1 的唯一类型是 charsigned charunsigned char。有可能他们可以使用bool,但bool 的大小不必为 1,然后你仍然在同一条船上,因为

int main()
{
    bool foo = 42;
    std::cout << foo << '\n';
}

将打印1,而不是42,因为任何非零都为真,而真则打印为1,但默认为。

我并不是说它不能完成,但是对于可以通过 cast or a function 处理的事情来说,这是很多工作


C++17 引入了std::byte,定义为enum class byte : unsigned char {};。所以它将是一个字节宽,但它不是字符类型。不幸的是,由于它是enum class,它有其自身的局限性。已为其定义了按位运算符,但没有内置的流运算符,因此您需要定义自己的输入和输出。这意味着您仍在转换它,但至少您不会与unsigned char 的内置运算符冲突。这给了你类似的东西

std::ostream& operator <<(std::ostream& os, std::byte b)
{
    return os << std::to_integer<unsigned int>(b);
}

std::istream& operator <<(std::istream& is, std::byte& b)
{
    unsigned int temp;
    is >> temp;
    b = std::byte{b};
    return is;
}

int main()
{
    std::byte foo{10};
    std::cout << foo;
}

【讨论】:

    【解决方案2】:

    由于 cmets 中存在一些错误信息,因此发布答案。

    uint8_t 可能是也可能不是 charunsigned char 的 typedef。它也可能是扩展整数类型(因此,不是字符类型)。

    编译器可以提供除标准要求的最小集合之外的其他整数类型(shortintlong 等)。例如,一些编译器提供 128 位整数类型。

    这也不会“与 C 冲突”,因为 C 和 C++ 都允许扩展整数类型。

    因此,您的代码必须允许这两种可能性。 cmets 中使用一元 + 的建议可行。

    我个人认为,如果标准要求uint8_t 不是是字符类型会更有意义,因为您注意到的行为是不直观的。

    【讨论】:

    • 是的,这正是我的想法。谢谢
    • 如果标准规定不应该是字符类型,应该使用什么类型?
    • @NathanOliver 不是字符类型的 8 位整数类型
    【解决方案3】:

    这是间接的标准行为,因为ostream 具有unsigned char 的重载,而unsigned char 是系统中相同类型uint8_t 的typedef。

    §27.7.3.1 [output.streams.ostream] 给出:

    template<class traits>
    basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, unsigned char);
    

    我在标准中找不到任何地方明确指出uint8_tunsigned char 必须 相同。只是在几乎所有实现中它们都占用 1 个字节是合理的。

     std::cout << std::boolalpha << std::is_same<uint8_t, unsigned char>::value << std::endl; // prints true
    

    要将值打印为整数,您需要一个不是unsigned char(或其他字符重载之一)的类型。对uint16_t 的简单转换可能就足够了,因为标准没有列出它的重载:

    uint8_t a = 65;
    std::cout << static_cast<uint16_t>(a) << std::endl; // prints 65
    

    Demo

    【讨论】:

    • 它们不必相同; uint8_t 可以是扩展整数类型。
    • 你不会找到任何能说明uint8_t 的底层类型必须是什么的东西,因为uint8_t 甚至可能不存在。
    • uint16_t 可能有同样的问题,在 16 位 char 的系统上。 (虽然很少见)
    • @MM:我试图传达他们不必相同,只是他们是和 OP(以及其他所有询问打印uint8_t)的人只是不走运他们的实现选择了unsigned char 作为它的typedef,它可能不会很快改变,因为它是一个合理的实现。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-02
    • 1970-01-01
    • 2020-12-09
    • 2019-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多