如何在不调用实现定义的行为的情况下将二进制数据写入文件?

Lon*_*ner 4 c c++

我正在编写这样的程序,将一些字节写入文件.

#include <fstream>

int main()
{
    char buffer[4] = {0, 0, 255, 255};
    std::ofstream f("foo.txt", std::ios_base::binary);
    f.write(buffer, sizeof buffer);
    f.close();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这很好,并在我的系统上给我预期的结果.

$ g++ -std=c++11 -pedantic -Wall -Wextra signedness.cc
$ ./a.out
$ cat foo.txt
$ od -t x1 foo.txt 
0000000 00 00 ff ff
0000004
Run Code Online (Sandbox Code Playgroud)

等效的C代码是:

#include <stdio.h>

int main()
{
    char buffer[4] = {0, 0, 255, 255};
    FILE *f = fopen("bar.txt", "wb");
    fwrite(buffer, sizeof *buffer, sizeof buffer, f);
    fclose(f);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

该程序也可以正常工作,并在我的系统上提供预期的输出.

我想知道上述将字节写入文件的方法是否合适.

C++ n3242.pdf的第4.7节(积分转换)在第3点中提到:

如果目标类型已签名,则如果可以在目标类型(和位字段宽度)中表示该值,则该值不会更改; 否则,该值是实现定义的.

C n1256.pdf的第6.3.1.3节(有符号和无符号整数)在第3点中提到:

否则,新类型将被签名,并且值无法在其中表示; 结果是实现定义的,或者引发实现定义的信号.

从这些提取中,当我将255指定为最后两个字节时,似乎我的程序调用实现定义的行为,char buffer[4]因为255不能在char类型中表示.如果我对此是正确的,那么将这四个字节写入文件的正确方法是什么?更改的类型buffercharunsigned char似乎并不在C++帮助,因为ofstream的的write()功能仍预期的第一个参数类型const char*.

Ben*_*igt 5

请记住,这ofstream只是一个typedef std::basic_ofstream<char>.

当你不想把事情当作对待时char,只需使用std::basic_ofstream<unsigned char>std::basic_ofstream<uint8_t>.

但最终,iostream是为格式化I/O而制作的.API对于二进制I/O来说非常糟糕(因为它不需要void*),而且速度也非常慢.此外,每个字符都由"facet"转换,这使得很难保证磁盘上输入字节和字节之间的1:1对应关系. basic_filebuf有点好,但不多.使用fopenfwrite仍然是一种非常有效的方法,即使在C++中也是如此.