【发布时间】:2013-04-22 00:29:36
【问题描述】:
现在我们有时都必须使用二进制数据。在 C++ 中,我们使用字节序列,并且从一开始char 就是我们的构建块。定义为具有 1 的sizeof,它是字节。所有库 I/O 函数默认使用char。一切都很好,但总有一点问题,一些奇怪的问题困扰了一些人 - 一个字节中的位数是实现定义的。
所以在 C99 中,决定引入几个 typedef 来让开发人员轻松表达自己,固定宽度的整数类型。当然是可选的,因为我们不想损害可移植性。其中uint8_t作为std::uint8_t迁移到C++11,是一个固定宽度的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_ts 读取为二进制数据。或者,我们中的一些人不关心字节中的位数并且对此感到满意。
但不幸的是,两个世界发生冲突,有时您必须将数据作为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 或 unsigned char,我们是否可以假设我们可以从 reinterpret_cast 执行 char* 到 @987654348 @ 反之亦然?便携吗?
这就是我的标准阅读能力让我失望的地方。我阅读了有关安全派生指针 ([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 可能缺少符号无关紧要,来回转换char* 和std::uint8_t* 是否可移植且安全?
我希望参考标准并附上解释。
编辑:谢谢,杰里·科芬。我将添加来自标准的引用([basic.lval],§3.10/10):
如果一个程序试图通过 Glvalue 访问一个对象的存储值,而不是其中一个 以下类型的行为未定义:
...
— char 或 unsigned char 类型。
EDIT2:好的,更深入。 std::uint8_t 不保证是 unsigned char 的 typedef。它可以实现为扩展无符号整数类型,扩展无符号整数类型不包含在第 3.10/10 节中。现在呢?
【问题讨论】:
-
老实说,你很难找到一个平台,其中
char不是在过去 20 多年左右制造的八位。跨度> -
我相信只有当你碰巧遇到一台没有 2 的补码表示的机器时才会不安全,在这种情况下你可能会得到意想不到的行为。但我希望看到真正知道答案的人。
-
那里有很多现代机器,比如只进行 32 位访问的 DSP,Joachim。
-
@FaTony:我很抱歉重复我自己,(请参阅我对 Jerry Coffin 的回答和其中的引用的评论),但你的句子“
std::uint8_t不能保证是@ 的typedef987654362@" 表明它可能是。其实不能。 C++11, 18.4.1 说uint8_t是一个typedef用于无符号整数类型。此外,3.9.1/3 定义了无符号整数类型,char不是其中之一,无论它是否可以容纳负值。 C99 声明相同。 -
我 asked a question a couple weeks ago 包含您的答案。看看R.'s answer,它解释了
uint8_t确实不需要需要与unsigned char具有相同的表示。
标签: c++ c++11 language-lawyer strict-aliasing uint8t