为什么将无符号字符* 转换为字符*需要reinterpret_cast?

Why does casting unsigned char* to char* require reinterpret_cast?

本文关键字:字符 reinterpret 需要 cast 转换 无符号 为什么      更新时间:2023-10-16

在stackoverflow上有很多关于读取和写入无符号字符的帖子。这些帖子建议使用reinterpret_castunsigned char*char *进行铸造。我想知道为什么需要它,因为两种类型的大小相同。

我在 Windows 和 Linux 上运行下面的代码,它工作正常(有或没有重新解释强制)。我错过了什么吗?有人可以发布一个没有reinterpret_cast就不起作用的代码吗?请注意,我说的是unsigned char(不是unsigned int)。

#include <iostream>
#include <fstream>
std::ofstream os;
unsigned char *buff = new unsigned char[3]{ 0xe4, 0xe1, 0xd4 };
os.open("image.jpg", std::ios::out | std::ios::binary);
os.write(reinterpret_cast<char*>(buff), 3 * sizeof(unsigned char));
// or: os.write((char*)(buff), 3 * sizeof(unsigned char));
os.close();
std::ifstream file;
memset(buff, 0, 3 * sizeof(unsigned char));
file.open("image.jpg", std::ios::in | std::ios::binary);
file.read(reinterpret_cast<char*>(buff), 3 * sizeof(unsigned char));
//or: file.read((char *)buff, 3 * sizeof(unsigned char));
file.close();

reinterpret_cast从来都不是"需要"的;reinterpret_cast能做的任何事情都可以通过C风格的演员表同样出色地完成。

特殊强制转换语法的存在是因为需要reinterpret_cast的东西往往是危险的东西,因此可能应该注意代码中的这些点。reinterpret_cast还可以防止意外删除const,这是 C 样式转换将允许您这样做。

为什么将无符号字符* 转换为字符*需要reinterpret_cast?

C++是一种类型化语言。尽管大小不同,但unsigned charchar是不同的类型。指向unsigned char的指针和指向char的指针也是单独的类型。

C++语言具有类型安全性。类型系统旨在通过禁止使用一个对象来防止您犯错误,就好像它是另一个不相关的类型一样。存在从某些类型到其他类型的隐式转换,这允许从另一种类型的对象创建一种类型的新对象1。此外,还有显式转换,当不存在隐式转换时,这些转换允许相同的转换。其中一个显式转换允许完全绕过类型系统:重新解释强制转换。

1同样,您可以将引用转换为其他类型的新引用。

charunsigned char没有关系。指向一个的指针不能隐式转换为指向另一个的指针。当参数具有另一种类型时,将一个作为参数传递是格式不正确的。如果您显式告诉编译器忽略类型系统,那么它将无法保护您免受使用错误类型的潜在错误。在这种情况下,重新解释是故意的,并且(据我所知)不是错误。正是这些有意的重新诠释,才是重新诠释选角存在的原因。

我想知道为什么需要它,因为两种类型的大小相同。

类型的含义并不完全由类型的大小来描述。


请注意,重新解释强制转换是不安全的。在处理重新解释的指针/引用时,必须遵循许多规则,违反这些规则中的任何一个都会导致未定义的行为。仅仅因为它在这里可能是正确的,所以不要假设每次遇到与类型不匹配时都可以使用重新解释转换。

2另请注意,如果不存在符合类型系统的转换,则 C 样式转换(char*)buff将重新解释转换。它还将进行常量铸造,而重新解释铸造则不行。这使得C型铸件更加不安全。通常建议显式使用您打算使用的强制转换类型(静态、重新解释或 const 强制转换),而不是 C 样式强制转换,后者执行其中一种或组合,具体取决于恰好适合哪种类型。

有人可以发布一个没有reinterpret_cast就无法工作的代码吗?

如果从程序中删除reinterpret_cast,则其格式不正确。该标准不保证它会起作用。如果您使用的编译器不扩展语言,则可能无法正常工作。例如,GCC 拒绝编译。

当然,任何需要reinterpret_cast的程序都可以重写以使用 C 样式转换来代替它,因为2


附言通常,在可能的情况下避免铸造是一种更好的设计。在这种情况下,您可以使用std::basic_ofstream<unsigned char>std::basic_ifstream<unsigned char>,并且不需要强制转换。