How to write binary data to a file without causing the behavior defined by the implementation?

I am writing a program to write several bytes to 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 great 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 

Equivalent C code:

 #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 great and gives the expected result on my system.

I want to know if the above method of writing bytes to a file is good.

Section 4.7 (Integral Transformations) C ++ n3242.pdf is mentioned in paragraph 3:

If the destination type is signed, the value does not change if it can be represented in the destination type (and bit width); otherwise, the value is determined by the implementation.

Section 6.3.1.3 (unsigned integers) C n1256.pdf is referred to in paragraph 3:

Otherwise, the new type is signed and the value cannot be represented in it; either the result is determined by the implementation or a signal determined by the implementation is generated.

From these extracts, it seems that my program causes the behavior defined by the implementation when I assign 255 as the last two bytes from char buffer[4] , because 255 cannot be represented in char type. If I'm right about this, then what would be the correct way to write these four bytes to a file? Changing the buffer type from char to unsigned char does not seem to help in C ++, since the write() function of the stream still expects the first parameter of type const char* .

+4
source share
3 answers

Remember that ofstream is just a typedef for std::basic_ofstream<char> .

If you don't want things to be treated as char , just use std::basic_ofstream<unsigned char> or std::basic_ofstream<uint8_t> .

Ultimately, iostreams are created for formatted I / O. The API is terrible for binary I / O (since it does not accept void* ), and is also incredibly slow. In addition, each character is transformed by a “facet,” which makes it difficult to guarantee a 1: 1 correspondence between input bytes and bytes on disk. basic_filebuf slightly better, but not much. Using fopen and fwrite is still a perfectly acceptable approach even in C ++.

+5
source

True, 255 cannot be represented in a signed char type, but you do not assign 255, you assign FF , which represents -1 .

You want to use uint8_t as defined in stdint.h , or at least use unsigned char .

0
source

“defined by implementation” is the language of the International Standard for C ++, which is a quasi-legal document designed to cover all possible cases.

Thus, it can work on a machine where char is 9 bits, so it is inherent in the act of processing binary data and files, which will be determined by some aspects. In the standard letter, you must study the documentation for each target machine and be sure that the required behavior is achieved in each case.

In the real world, every modern general-purpose processor has 8-bit bytes and uses arithmetic with the addition of a 2s complement, so you have nothing to worry about if you are not aiming for a very specific situation.

-1
source

Source: https://habr.com/ru/post/1434715/


All Articles