Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/162.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 重新解释char*和std::uint8\u t*-安全之间的强制转换?_C++_C++11_Language Lawyer_Strict Aliasing_Uint8t - Fatal编程技术网

C++ 重新解释char*和std::uint8\u t*-安全之间的强制转换?

C++ 重新解释char*和std::uint8\u t*-安全之间的强制转换?,c++,c++11,language-lawyer,strict-aliasing,uint8t,C++,C++11,Language Lawyer,Strict Aliasing,Uint8t,现在我们有时都要处理二进制数据。在C++中,我们使用字节序列,因为开始 char 是我们的构建块。定义为sizeof为1,它是字节。默认情况下,所有库I/O函数都使用char。一切都很好,但总是有一个小问题,一个小奇怪的问题困扰着一些人-一个字节中的位数是由实现定义的 因此,在C99中,决定引入几个typedef,让开发人员轻松地表达自己,即固定宽度的整数类型。当然,这是可选的,因为我们从不想损害可移植性。其中,uint8\u t,作为std::uint8\u t迁移到C++11,这是一种固定宽

现在我们有时都要处理二进制数据。在C++中,我们使用字节序列,因为开始<代码> char 是我们的构建块。定义为
sizeof
为1,它是字节。默认情况下,所有库I/O函数都使用
char
。一切都很好,但总是有一个小问题,一个小奇怪的问题困扰着一些人-一个字节中的位数是由实现定义的

因此,在C99中,决定引入几个typedef,让开发人员轻松地表达自己,即固定宽度的整数类型。当然,这是可选的,因为我们从不想损害可移植性。其中,
uint8\u t
,作为
std::uint8\u t
迁移到C++11,这是一种固定宽度的8位无符号整数类型,对于真正想使用8位字节的人来说是完美的选择

因此,开发人员接受了新工具,并开始构建库,明确表示他们接受8位字节序列,如
std::uint8*
std::vector
或其他

但是,也许经过深思熟虑,标准化委员会决定不要求实现
std::char_traits
,因此禁止开发人员轻松、可移植地实例化,比如说,
std::basic_fstream
,并将
std::uint8_t
作为二进制数据轻松读取。或者,我们中的一些人不关心字节中的位数,并且对此感到满意

但不幸的是,两个世界发生了冲突,有时您必须将数据作为
char*
并将其传递给需要
std::uint8*
的库。但是,等等,你说,
char
不是变量位吗?而且
std::uint8\u t
不是固定为8吗?它会导致数据丢失吗

嗯,这方面有一个有趣的标准。定义为只保存一个字节的
char
,字节是内存中可寻址的最低块,因此不能存在位宽小于
char
的类型。接下来,它被定义为能够容纳UTF-8代码单元。这给了我们最小的-8位。现在我们有了一个typedef,它需要8位宽,还有一个至少8位宽的类型。但还有其他选择吗?是,
无符号字符
。请记住,
char
的签名是由实现定义的。还有其他类型的吗?谢天谢地,没有。所有其他的积分类型都需要8位以外的范围

最后,
std::uint8\t
是可选的,这意味着使用此类型的库如果未定义,将不会编译。但如果它编译了呢?我可以非常自信地说,这意味着我们所处的平台具有8位字节和
CHAR\u bit==8

一旦我们知道我们有8位字节,
std::uint8\u t
被实现为
char
unsigned char
,我们可以假设我们可以从
char*
重新解释
std::uint8\u*
,反之亦然?它是便携式的吗

这就是我的标准阅读技能让我失望的地方。我阅读了有关安全派生指针的内容(
[basic.stc.dynamic.safety]
),据我所知,以下内容:

std::uint8_t* buffer = /* ... */ ;
char* buffer2 = reinterpret_cast<char*>(buffer);
std::uint8_t buffer3 = reinterpret_cast<std::uint8_t*>(buffer2);
std::uint8_t*缓冲区=/*…*/;
char*buffer2=重新解释强制转换(buffer);
std::uint8\u t buffer3=重新解释强制转换(buffer2);
如果我们不触摸缓冲区2,它是安全的。如果我错了,请纠正我

因此,考虑到以下先决条件:

  • CHAR\u位==8
  • 定义了
    std::uint8\t
假设我们使用的是二进制数据,并且字符的潜在符号缺失无关紧要,那么来回转换
char*
std::uint8\u t*
是否方便安全

如能参考本标准并加以解释,我将不胜感激

编辑:谢谢,杰瑞·科芬。我将添加标准([basic.lval],§3.10/10)中的引用:

如果程序试图通过glvalue访问对象的存储值,而不是 以下类型的行为未定义:

-字符或无符号字符类型


编辑2:好的,再深入一点
std::uint8_t
不保证是
无符号字符的typedef
。它可以实现为扩展无符号整数类型,扩展无符号整数类型不包括在§3.10/10中。现在怎么办?

如果
uint8\u t
确实存在,那么本质上唯一的选择就是它是
无符号字符
的类型定义(或者
字符
,如果它恰好是无符号的)。没有任何东西(除了一个位字段)比
char
表示的存储量少,唯一一种可以小到8位的类型是
bool
。下一个最小的正常整数类型是
short
,它必须至少为16位

因此,如果存在
uint8\t
,那么实际上只有两种可能性:要么将
无符号字符
转换为
无符号字符
,要么将
有符号字符
转换为
无符号字符

前者是身份转换,所以显然是安全的。后者属于§3.10/10中为访问任何其他类型的字符序列或无符号字符而给出的“特殊豁免”,因此它也给出了定义的行为

由于它既包括
char
又包括
unsigned char
,因此将其作为char序列进行访问的转换也会给出定义的行为

编辑:就Luc提到的扩展整数类型而言,我不确定您如何在这种情况下应用它来获得差异。C++是C~(99)标准,定义为<代码> uTI8*T ,因此,其余的引文来自C99 .< §6.2.6.1/3规定
无符号字符
应使用纯二进制表示
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);