现在我们有时都必须使用二进制数据。在 C++ 中,我们使用字节序列,并且从一开始char
是我们的基石。定义为有sizeof
为 1 时,它是字节。所有库 I/O 函数都使用char
默认情况下。一切都很好,但总是有一点担心,一点奇怪的事情困扰着一些人——一个字节中的位数是实现定义的。
因此,在 C99 中,决定引入几种 typedef,以便开发人员轻松表达自己的固定宽度整数类型。当然,这是可选的,因为我们不想损害可移植性。他们之中,uint8_t
,迁移到 C++11 中为std::uint8_t
,一种固定宽度的 8 位无符号整数类型,对于真正想要使用 8 位字节的人来说是完美的选择。
因此,开发人员接受了新工具并开始构建库,明确声明他们接受 8 位字节序列,如std::uint8_t*
, std::vector<std::uint8_t>
或其他方式。
但是,也许经过深思熟虑,标准化委员会决定不要求实施std::char_traits<std::uint8_t>
因此禁止开发人员轻松且可移植地实例化,例如,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*
and std::uint8_t*
来回,假设我们正在处理二进制数据并且可能缺乏符号char
没关系吗?
我希望能参考该标准并附上解释。
编辑:谢谢,杰里·科芬。我将添加标准中的引用([basic.lval],§3.10/10):
如果程序尝试通过除其中之一以外的泛左值来访问对象的存储值
以下类型的行为未定义:
...
— char 或 unsigned char 类型。
EDIT2:好的,更深入。std::uint8_t
不保证是 typedefunsigned char
。它可以实现为扩展无符号整数类型§3.10/10 中不包含扩展无符号整数类型。现在怎么办?