gpt4 book ai didi

c++ - char* 和 std::uint8_t* 之间的 reinterpret_cast - 安全吗?

转载 作者:IT老高 更新时间:2023-10-28 13:58:35 29 4
gpt4 key购买 nike

现在我们有时都必须使用二进制数据。在 C++ 中,我们使用字节序列,并且从一开始 char是我们的基石。定义为 sizeof 1,它是字节。并且所有库 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_t s 作为二进制数据。或者,我们中的一些人不关心字节中的位数并且对此感到满意。

但不幸的是,两个世界发生冲突,有时您必须将数据视为 char*并将其传递给期望 std::uint8_t* 的库.但是等等,你说,不是char可变位和std::uint8_t固定为8?会不会导致数据丢失?

嗯,这有一个有趣的标准话。 char定义为恰好保存一个字节,并且字节是内存的最低可寻址 block ,因此不能有位宽小于 char 的类型.接下来,它被定义为能够保存 UTF-8 代码单元。这给了我们最小值 - 8 位。所以现在我们有一个需要 8 位宽的 typedef 和一个至少 8 位宽的类型。但是有替代品吗?是的,unsigned char .请记住 char 的签名是实现定义的。还有其他类型吗?谢天谢地,没有。所有其他整数类型都需要超出 8 位的范围。

最后,std::uint8_t是可选的,这意味着如果未定义使用此类型的库将不会编译。但是如果它编译呢?我可以非常自信地说,这意味着我们在一个有 8 位字节和 CHAR_BIT == 8 的平台上。 .

一旦我们知道,我们有 8 位字节,std::uint8_t实现为 charunsigned char , 我们可以假设我们可以做到reinterpret_cast来自 char*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):

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

...

— a char or unsigned char type.

EDIT2:好的,更深入。 std::uint8_t不保证是 unsigned char 的 typedef .它可以实现为扩展无符号整数类型,扩展无符号整数类型不包含在第 3.10/10 节中。现在呢?

最佳答案

好吧,让我们变得真正迂腐。看完this , thisthis ,我非常有信心了解这两个标准背后的意图。

所以,从 std::uint8_t*char* 执行 reinterpret_cast 然后解除对结果指针的引用是 safeportable 并且被 [basic.lval] 明确允许.

但是,从 char*std::uint8_t* 执行 reinterpret_cast 然后解除对结果指针的引用是违反 严格的别名规则,如果 std::uint8_t 实现为扩展无符号整数类型,则为未定义行为

但是,有两种可能的解决方法,第一种:

static_assert(std::is_same_v<std::uint8_t, char> ||
std::is_same_v<std::uint8_t, unsigned char>,
"This library requires std::uint8_t to be implemented as char or unsigned char.");

有了这个断言,你的代码将不会在平台上编译,否则会导致未定义的行为。

第二:

std::memcpy(uint8buffer, charbuffer, size);

Cppreference表示 std::memcpyunsigned char 数组的形式访问对象,因此它是 safeportable。 p>

重申一下,为了能够在 char*std::uint8_t* 之间 reinterpret_cast 并使用结果指针 便携安全以 100% 符合标准的方式,必须满足以下条件:

  • CHAR_BIT == 8
  • std::uint8_t 已定义。
  • std::uint8_t 实现为 charunsigned char

实际上,上述条件在 99% 的平台上都为真,并且很可能没有平台上前两个条件为真而第三个条件为假。

关于c++ - char* 和 std::uint8_t* 之间的 reinterpret_cast - 安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16260033/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com