现在我们有时都必须使用二进制数据。在 C++ 中,我们使用字节序列,从一开始char
就是我们的构建块。定义为sizeof
1,它是字节。char
并且默认使用所有库 I/O 函数。一切都很好,但总是有一点点担心,一些奇怪的东西会困扰一些人——一个字节中的位数是实现定义的。
所以在 C99 中,决定引入几个 typedef 让开发人员可以轻松地表达自己,固定宽度的整数类型。当然是可选的,因为我们不想损害可移植性。其中uint8_t
, 迁移到 C++11 作为std::uint8_t
, 一个固定宽度的 8 位无符号整数类型,对于真正想要使用 8 位字节的人来说是完美的选择。
因此,开发人员接受了新工具并开始构建库,这些库明确地声明他们接受 8 位字节序列,asstd::uint8_t*
或std::vector<std::uint8_t>
其他。
但是,也许经过深思熟虑,标准化委员会决定不要求实现,std::char_traits<std::uint8_t>
因此禁止开发人员轻松便携地将 s 实例化,例如,std::basic_fstream<std::uint8_t>
轻松地std::uint8_t
将 s 读取为二进制数据。或者也许,我们中的一些人不关心字节中的位数并且对此感到满意。
但不幸的是,两个世界发生冲突,有时您必须将数据作为char*
并将其传递给期望的库std::uint8_t*
。但是等等,你说,不是char
可变位并且std::uint8_t
固定为8吗?会不会导致数据丢失?
好吧,这有一个有趣的标准语。定义为恰好保存一个字节并且字节是内存的char
最低可寻址块,因此不能有位宽小于char
. 接下来,它被定义为能够保存 UTF-8 代码单元。这给了我们最小值 - 8 位。所以现在我们有一个需要 8 位宽的 typedef 和一个至少 8 位宽的类型。但是有替代品吗?是的,unsigned char
。请记住,签名char
是实现定义的。还有其他类型吗?谢天谢地,没有。所有其他整数类型的所需范围都在 8 位之外。
最后,std::uint8_t
是可选的,这意味着如果未定义使用此类型的库将不会编译。但是如果它编译呢?我可以非常自信地说,这意味着我们处于一个具有 8 位字节和CHAR_BIT == 8
.
一旦我们知道我们有 8 位字节,即std::uint8_t
实现为char
or unsigned char
,我们是否可以假设我们可以执行reinterpret_cast
from char*
to std::uint8_t*
,反之亦然?它是便携式的吗?
这就是我的标准阅读技巧让我失望的地方。我阅读了有关安全派生指针 ( [basic.stc.dynamic.safety]
) 的内容,据我所知,以下内容:
std::uint8_t* buffer = /* ... */ ;
char* buffer2 = reinterpret_cast<char*>(buffer);
std::uint8_t buffer3 = reinterpret_cast<std::uint8_t*>(buffer2);
如果我们不碰它是安全的buffer2
。如我错了请纠正我。
因此,鉴于以下先决条件:
CHAR_BIT == 8
std::uint8_t
被定义为。
假设我们正在处理二进制数据并且可能缺少符号无关紧要,那么它是否可移植且安全char*
?std::uint8_t*
char
我将不胜感激对标准的参考和解释。
编辑:谢谢,杰里·科芬。我将添加来自标准的引用([basic.lval],§3.10/10):
如果程序尝试通过非下列类型之一的泛左值访问对象的存储值,则行为未定义:
...
— char 或 unsigned char 类型。
EDIT2:好的,更深入。std::uint8_t
不保证是unsigned char
. 它可以实现为扩展无符号整数类型,扩展无符号整数类型不包含在 §3.10/10 中。现在怎么办?