std::cout把uint8_t当作字符处理

5
如果我运行这段代码:
std::cout << static_cast<uint8_t>(65);

它将输出:

A

这是数字65的ASCII等效值。 这是因为uint8_t被简单地定义为:

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

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

我无法理解这种逻辑:如果我想打印一个uint8_t变量的值,它会作为字符打印。

P.S. 我正在使用MSVS 2013。


3
是的,这是标准行为。如果你想把 uint8_t 作为小的无符号整数使用,在输出之前需要进行类型转换。例如:static_cast<uint32_t>(some_uint8_t_variable) - Some programmer dude
2
@HumamHelfawi - 没有大小为1且不是char变体的原始类型。是的,C++可以添加新类型,但这将与这些typedef起源的C冲突。 - Pete Becker
2
std::cout << +static_cast<uint8_t>(65);会实现你想要的功能。 - Pete Becker
2
“+”是一元运算符,它的作用很像一元运算符“-”,但它不会否定值。它通常被视为无意义的,但在这里它确实有用,因为作为算术运算符,编译器将其参数提升为“int”。因此,“+x”等同于“(int)x”。 - Pete Becker
@NathanOliver 我明白了。但是正如Pete所说,这似乎是C兼容性问题。我看到除了char或全新类型之外别无选择。 - Humam Helfawi
显示剩余9条评论
3个回答

5
这种行为是标准的,如果uint8_tunsigned char 的 typedef,则始终会将字符打印为 std::ostream 具有 unsigned char 的重载,并输出变量内容作为字符。
应该没有更好的方法来定义 uint8_t 以保证其被视为数字而非字符。要做到这一点,C++委员会必须引入一个新的基本类型。目前,唯一具有等于1的sizeof()的类型是charsigned charunsigned char 。他们可能会使用一个bool,但是bool不一定要有大小为1的大小,然后你仍然面临同样的问题。
int main()
{
    bool foo = 42;
    std::cout << foo << '\n';
}

将打印1,而不是42,因为任何非零值都为true,并且默认情况下true将打印为1

我不是说这不能完成,但对于可以使用强制转换或函数处理的事情而言,这需要大量工作


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;
}

3

在评论中有一些错误信息,因此我会发表回答。

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

编译器可能提供标准所需的最小整数类型集之外的其他整数类型(例如 shortintlong 等)。例如,某些编译器提供了 128 位整数类型。

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

因此,您的代码必须考虑这两种可能性。评论中使用一元 + 的建议是可行的。

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


是的,这正是我所想的。谢谢。 - Humam Helfawi
如果标准规定不应该使用字符类型,那么应该使用什么类型? - NathanOliver
@NathanOliver 一个不是字符类型的8位整数类型 - M.M

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可以是扩展整数类型。 - M.M
你不会找到任何明确说明uint8_t底层类型必须是什么的东西,因为uint8_t可能甚至不存在。 - NathanOliver
uint16_t 在一个具有 16 位 char 的系统上可能会遇到相同的问题。(尽管这种情况很少见) - M.M
@M.M:我想表达的是它们不必相同,只是它们现在是这样的,而OP(以及所有询问如何打印uint8_t的人)只是不幸地选择了unsigned char作为其typedef,并且它可能不会很快改变,因为这是一个合理的实现。 - AndyG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接