C++ 严格别名和二进制I/O 让我们考虑下面的(简化)代码来读取二进制文件的内容: struct Header { char signature[8]; uint32_t version; uint32_t numberOfSomeChunks; uint32_t numberOfSomeOtherChunks; }; void readFile(std::istream& stream) { // find total size of the file, in bytes: stream.seekg(0, std::ios::end); const std::size_t totalSize = stream.tellg(); // allocate enough memory and read entire file std::unique_ptr<std::byte[]> fileBuf = std::make_unique<std::byte[]>(totalSize); stream.seekg(0); stream.read(reinterpret_cast<char*>(fileBuf.get()), totalSize); // get the header and do something with it: const Header* hdr = reinterpret_cast<const Header*>(fileBuf.get()); if(hdr->version != expectedVersion) // <- Potential UB? { // report the error } // and so on... } struct头文件 { 字符签名[8]; uint32_t版本; uint32_t数量的体块; uint32\t其他块的数量; }; 无效读取文件(std::istream&stream) { //查找文件的总大小(以字节为单位): stream.seekg(0,std::ios::end); const std::size_t totalSize=stream.tellg(); //分配足够的内存并读取整个文件 std::unique_ptr fileBuf=std::make_unique(totalSize); 流。seekg(0); read(reinterpret_cast(fileBuf.get()),totalSize); //获取标题并对其进行处理: const Header*hdr=reinterpret_cast(fileBuf.get()); 如果(hdr->version!=expectedVersion)//version!=expectedVersion)//

C++ 严格别名和二进制I/O 让我们考虑下面的(简化)代码来读取二进制文件的内容: struct Header { char signature[8]; uint32_t version; uint32_t numberOfSomeChunks; uint32_t numberOfSomeOtherChunks; }; void readFile(std::istream& stream) { // find total size of the file, in bytes: stream.seekg(0, std::ios::end); const std::size_t totalSize = stream.tellg(); // allocate enough memory and read entire file std::unique_ptr<std::byte[]> fileBuf = std::make_unique<std::byte[]>(totalSize); stream.seekg(0); stream.read(reinterpret_cast<char*>(fileBuf.get()), totalSize); // get the header and do something with it: const Header* hdr = reinterpret_cast<const Header*>(fileBuf.get()); if(hdr->version != expectedVersion) // <- Potential UB? { // report the error } // and so on... } struct头文件 { 字符签名[8]; uint32_t版本; uint32_t数量的体块; uint32\t其他块的数量; }; 无效读取文件(std::istream&stream) { //查找文件的总大小(以字节为单位): stream.seekg(0,std::ios::end); const std::size_t totalSize=stream.tellg(); //分配足够的内存并读取整个文件 std::unique_ptr fileBuf=std::make_unique(totalSize); 流。seekg(0); read(reinterpret_cast(fileBuf.get()),totalSize); //获取标题并对其进行处理: const Header*hdr=reinterpret_cast(fileBuf.get()); 如果(hdr->version!=expectedVersion)//version!=expectedVersion)//,c++,language-lawyer,C++,Language Lawyer,问题是:我的解释正确吗 对 如果是,如何修复此代码 您已经知道memcpy是一种解决方案。但是,您可以通过直接读取header对象跳过memcpy和额外内存分配: Header h; stream.read(reinterpret_cast<char*>(&h), sizeof h); 头h; stream.read(重新解释铸件(&h),尺寸为h; 请注意,以这种方式读取二进制文件意味着文件的整数表示形式必须与CPU的表示形式相匹配。这意味着该文件不能移植到具有不同C

问题是:我的解释正确吗

如果是,如何修复此代码

您已经知道memcpy是一种解决方案。但是,您可以通过直接读取header对象跳过memcpy和额外内存分配:

Header h;
stream.read(reinterpret_cast<char*>(&h), sizeof h);
头h;
stream.read(重新解释铸件(&h),尺寸为h;

请注意,以这种方式读取二进制文件意味着文件的整数表示形式必须与CPU的表示形式相匹配。这意味着该文件不能移植到具有不同CPU体系结构的系统

如何修复此代码


等待,直到接受允许在
char
/
无符号char数组中创建隐式对象或类似的操作。

@Hans Passant在相反的情况下(通过
std::byte*
访问
)。但在这里,我认为您链接到的规则不适用。@curiousguy内存是用
new
分配的,因此与最大本机对齐。
没有过度对齐,所以没有问题。正如joe指出的,别名仍然是一个问题。我忘记了分配给b的字节数组是保证的与字节数组类型的变量不同,e对齐。显示的特定头不太可能有问题,但一般来说,直接从文件读取到结构中也是不安全的,因为结构的成员之间可能存在填充,而该填充不是文件格式的一部分。“这意味着该文件不能移植到具有不同CPU体系结构的系统。"-这就是为什么您应该定义文件中使用的endian,然后根据需要在内存中读取/写入字段时交换字段的endian。OP的原始代码无论如何也会遇到同样的问题。@RemyLebeau确实,我的建议与OP的原始程序尝试做的相同,但定义良好的情况除外一种对现代高水平(和疯狂)的诠释C++是因为你没有一个字节数组,所以你不能在这里做指针运算。@好奇人,这里没有指针运算的问题。你可以在指针上执行指针运算,而不是指向一个字节。@ ReMyLeBeAu,根据一些PPL,指针指向字节的算术有问题。您尚未声明或创建的位置(使用运算符new)字节数组:哪个数组正在使用指针算法进行迭代?据我所知,P0593将用于将
头的表示形式
读入分配有
malloc
或任何其他函数的存储区域,该函数在存储区域中不创建对象,但
std::make_unique
创建N
std::byte
(又称
unsigned char
)对象在存储区域中,因此违反了严格的别名规则。@zwol与OPs case非常相似的情况是P0593的动机。P0593建议允许对象出现在
std::byte
数组中:“我们建议至少将以下操作指定为隐式创建对象:创建char、unsigned char或std::byte数组在该数组中隐式创建对象。”
Header h;
stream.read(reinterpret_cast<char*>(&h), sizeof h);