字节类型:std::byte vs std::uint8_t vs unsigned char vs char vs std::bitset<8>

Jan*_*tke 4 c++ byte c++17 std-byte

C++ 有很多类型模糊地描述相同的事物。假设我们正在针对一个字节为 8 位的架构进行编译,则以下所有类型都大致相似:

  • std::byte
  • std::uint8_t
  • std::bitset<8>
  • unsigned char(8 位)
  • char(8 位)

如果一个字节是 8 位,那么所有这些类型或多或少可以互换吗?如果没有,什么时候需要使用一个而不是另一个?

我经常在 Stack Overflow 上看到诸如将十六进制字符串转换为字节数组之类的问题,其中有人使用std::uint8_tcharunsigned char其他类型来表示“字节”。这只是风格偏好的问题吗?


注意:此问答旨在成为社区常见问题解答,鼓励进行编辑。std::byte尽管 C++17 已经引入了这似乎使得选择变得显而易见,但何时使用“字节”的类型以及为什么的问题始终出现。std::bitset提供一个常见问题解答来解决有关、std::uint8_t等作为“字节”的所有误解是很有用的。鼓励编辑。

Jan*_*tke 9

对于 8 位架构,所有列出的类型都隐约相似,因为它们都对 8 位的东西进行建模。然而,用例本质上是不同的,并且只有其中一些类型保证有特殊属性,使它们可以用作字节类型。

\n

概述

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
类型定义目的
std::byteenum class byte : unsigned char {};规范字节类型
\xe2\x9c\x94\xef\xb8\x8f 所有特殊属性
unsigned char基本型字符/旧字节类型/小型算术类型
\xe2\x9c\x94\xef\xb8\x8f 所有特殊属性
signed char基本型字符/小算术类型
\xe2\x9d\x8c 无特殊属性
char基本类型
signed char, 与或相同的基础类型unsigned char
一个字符
\xe2\x9a\xa0\xef\xb8\x8f 只有一些特殊属性
char8_t基本类型
底层类型unsigned char
UTF-8 字符
\xe2\x9d\x8c 无特殊属性
std::uint8_ttypedef unsigned char uint8_t;
(这并不能保证,只是最
常见的实现。)
8 位无符号算术
\xe2\x9a\xa0\xef\xb8\x8f 不保证特殊属性
std::bitset<8>template <std::size_t N>
class bitset;
8 位组;可能比 8 位宽
\xe2\x9d\x8c 无特殊属性
\n
\n

请参阅问题末尾的附录,了解所有这些特殊属性(按类型)的列表。

\n

std::byte(C++17)

\n

这是C++ 中的规范字节类型。每当你不得不问自己这个问题“我应该使用哪种类型来表示这些字节?” std::byte就是答案。

\n

请注意,这std::byte是非常特殊的,因为有许多放宽允许您以其他未定义的方式使用该类型。例如,严格的别名规则对于std::byte( [basic.lval] p11 ) 来说是宽松的,这意味着您可以将任何对象作为 s 数组进行检查std::byte

\n

大多数其他类型不具有这些特殊功能,并且尝试将它们用作字节将是未定义的行为。

\n

与原始内存操作一样std::byte,许多较旧的 API(例如<iostream>库)早于它,并且不是围绕它设计的。\n该类型也有些笨重(例如,my_byte == 0不可能)。\n不要尝试强制使用它与不是为std::byte.

\n
\n

另请参阅:C++ 中是否有“字节”数据类型?, std::byte 的用途是什么?,\n P0298 - 字节类型定义*

\n

unsigned char

\n

这是 C++17 之前最接近“字节”的东西。\nunsigned char具有 a 所具有的所有特殊属性std::byte

\n

然而,这个名字非常令人困惑,并且在某些上下文中它也被视为一个字符。例如,std::ostream::operator<<将其打印为 ASCII 字符,而不是打印其数值。另外,用 进行算术运算unsigned char会将其提升到int任何操作之前,这对于“字节”来说似乎不合适。

\n

总而言之,它是一种软弱无力的类型,同时又是字节、字符和算术类型。更喜欢std::bytecharstd::uint8_t、 或。std::uint_least8_t

\n
\n

另请参阅:如何在需要旧式 unsigned char 的地方使用新的 std::byte 类型?

\n

signed char

\n

签名对应的 也unsigned char同样令人困惑。它几乎没有std::byte和 所unsigned char具有的特殊属性,并且是算术类型和字符类型的奇怪组合。也应该避免这种情况。

\n

一个更好的选择是std::int_least8_t它也是带符号的,并且也保证至少 8 位宽,但它没有作为字符的奇怪含义。

\n
\n

另请参阅:signed / unsigned char 之间的区别

\n

char

\n

signed char这是一个不同的类型,与或具有相同的基础类型。\n它具有和unsigned char的大部分(但不是全部)特殊属性。\n例如,与 不同,它不提供存储 ( [intro.object] p3 )在 a 中创建的对象。unsigned charstd::byteunsigned charchar[]

\n

char应该用于顾名思义:一个字符

\n
\n

另请参见:char!=(signed char)、char!=(unsigned char)

\n

char8_t(C++20)

\n

最初有一些关于此类型具有类似于 的特殊属性的讨论char,但最终没有。\n它的基础类型是unsigned char,但与 不同std::byte,这并不意味着它继承了它的任何属性。

\n

它应该用作 UTF-8 字符,可能在 UTF-8 编码的字符串中。

\n

std::uint8_t(C++11)

\n

这种类型是从 C 开始的设计错误。\n虽然不能保证这一点,但它通常作为类型别名实现,例如

\n
typedef unsigned char uint8_t;\n
Run Code Online (Sandbox Code Playgroud)\n

这意味着它具有unsigned char实际中具有的特殊属性(因为所有编译器都这样实现它),但标准并不能保证这些。\n它可以为所有其他类型别名的事实也可能使其对性能产生不利影响,与它是唯一类型的别名相比。

\n

需要注意的一件事是,C++ 中的字节不能保证为 8 位。\n许多人使用它是因为它提供了真正8 位std::uint8_t的安全性。\n但是,它是可选的,并且在以下平台上不存在:一个字节比 8 位宽,因此它的可移植性并不比:std::uint8_t

\n
#include <climits>\nstatic_assert(CHAR_BIT == 8); // ... and use unsigned char or char as a byte type\n
Run Code Online (Sandbox Code Playgroud)\n

对于更可移植的 8 位算术类型,有std::uint_fast8_tstd::uint_fast8_t,它们保证存在,但可能比 8 位宽。

\n

注意std::uint8_t,、、std::uint_least8_tstd::uint_fast8_t可能全部提升为int, 就像unsigned char

\n
\n

另请参阅:uint8_t 与 unsigned char哪些平台有除 8 位字符之外的其他内容?

\n

std::bitset<8>

\n

这是距离“字节”类型最远的类型。\n它对位序列或一组数字进行建模,具体取决于视角。

\n

Astd:bitset<8>至少与int大多数实现中一样大,因此它甚至不是 8 位大。仅将此类型用于其名称所示的用途:一组位。它不是一个字节。

\n

结论

\n

std::byte是唯一模拟字节的类型,仅此而已。只要有可能,就应优先将其作为字节类型。\n所有其他类型要么缺少关键属性,要么具有与字节完全不同的用途。

\n

附录

\n

std::byte普通字符类型的特殊属性

\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n
部分受影响的类型特殊属性
[介绍对象] p3unsigned char[],std::byte[]数组为放置在其中的对象提供存储
[介绍对象] p13unsigned char[],std::byte[]数组在其生命周期开始时隐式地在内部创建对象
[基础生活] p6.4简历 char*简历 unsigned char*简历 std::byte*static_cast允许存在指向生命周期之外的对象的指针
[基本信息]无符号普通字符类型std::byte初始化和分配时允许不确定的结果
[基本.类型.一般] p2char[], unsigned char[],std::byte[]可简单复制的对象可以通过数组传输其值
[基本.lval] p11.3char, unsigned char,std::byte宽松的严格别名
[expr.new] p16char[], unsigned char[],std::byte[]新表达式中更严格的对齐
[位.cast] p2无符号普通字符类型std::byte允许不确定的结果std::bit_cast
\n
\n

注意:尚不清楚无符号普通字符类型的实际含义。请参阅社论第 5070 期

\n