I am writing a program like this to write some bytes into a file.
#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;
}
This works fine and gives me the expected result on my system.
$ 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
The equivalent C code would be:
#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;
}
This program also works fine and gives the expected output on my system.
I want to know if the above way of writing bytes into a file is okay.
Section 4.7 (Integral conversions) of C++ n3242.pdf mentions in point 3:
If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.
Section 6.3.1.3 (signed and unsigned integers) of C n1256.pdf mentions in point 3:
Otherwise, the new type is signed and the value cannot be represented in it; either the
result is implementation-defined or an implementation-defined signal is raised.
From these extracts, it seems like my program invokes implementation-defined behavior when I assign 255 as the last two bytes of char buffer[4] because 255 cannot be represented in char type. If I am right about this, then what would be the right way to write these four bytes into a file? Changing the type of buffer from char to unsigned char doesn’t seem to help in C++ because ofstream’s write() function still expects the first parameter to of type const char*.
Remember that
ofstreamis just a typedef forstd::basic_ofstream<char>.When you don’t want things being treated as
char, just usestd::basic_ofstream<unsigned char>orstd::basic_ofstream<uint8_t>.Ultimately, though, iostreams are made for formatted I/O. The API is horrible for binary I/O (since it doesn’t take
void*) and also incredibly slow. Furthermore, every character is converted by a “facet”, making it hard to guarantee 1:1 correspondence between input bytes and bytes on disk.basic_filebufis a little better, but not much. Usingfopenandfwriteis still a perfectly valid approach, even in C++.