10
votes

I'm writing some code that uses the fstream read() function and this function expects a char* as the buffer. Later on, I want to work with the bytes in this buffer as unsigned chars, so I'm either going to have to: 1. declare the buffer as a char* and then do static_casts for each element later, 2. declare the buffer as unsigned char* and then do a reinterpret_cast when I pass it to the read function, or 3. declare the buffer as a char* and also create a casted pointer that I use for accessing the buffer as an unsigned char.

Here's a snippet:

char* buf = new char[512];
unsigned char* ubuf = reinterpret_cast<unsigned char*>(buf);

    fstream myfile;

    myfile.open("foo.img");

    myfile.seekg(446);
    myfile.read(buf, 16);
    //myfile.read(reinterpret_cast<char*>(buf), 16);

int bytes_per_sector = ubuf[1] << 8 | ubuf[0];
...

I like this way because I only have to cast once and I can just access the buffer as either type without doing a cast every time. But, Is this a good practice? Is there anything that can go wrong here? Using reinterpret_cast makes me a little nervous because I don't normally use it and I've been told to be careful with it so many times.

1
This is one of the very few cases where reinterpret_cast is actually safe and makes sense. - Konrad Rudolph
@Deduplicator Ugh. Please don’t recommend using C-style casts. Do consider those deprecated in C++. It’s safe in this situation, but it’s a much simpler rule to just ban them outright, and to avoid the potential of confusion. And the reinterpret_cast, being more explicit, makes the code more readable as well, since it clearly tells the reader which cast is being performed. - Konrad Rudolph
@Deduplicator The C++ casts replace C cast. Use of C cast is neither helpful nor justified since there is always a more specific C++ cast. There is no reason to ever use a C cast. Your "might be sufficiently less bulky" doesn't make sense since 1. a C cast would just do what the appropriate C++ cast would and 2. in this case that's nothing. - philipxy
@Deduplicator The point both philipxy and I were making is that reinterpret_cast is explicit and therefore increases readability even if not type safety. A reinterpret_cast has a well-defined meaning. A C-style cast, by contrast, doesn’t. It can mean any of a number of things, so using it in code obscures the actual semantics from the reader. That’s generally acknowledged to be an incredibly bad idea. - Konrad Rudolph
... Now, if I had to re-design C++, I would love to downgrade c-style-cast to implicit, function-style to implicit+any ctor+conversion-operator (it's a shame implicit_cast isn't in C++14), and make the other 4 kinds (static, const, dynamic, reinterpret) short and concise variants. - Deduplicator

1 Answers

9
votes

In this case a reinterpret_cast is fine, for 2 reasons:

  1. The (signed) char and unsigned char types are required to have the same "representation and alignment". This means that there is not going to be a difference in the data (it will be bit-per bit exact), or how long the buffer is interpreted to be.

  2. File reading functions typically use char* just as a generic data-access type. They can't use void* because the type void has an specifically undefined length and representation. char, However, does. So they can use it to read/write a series of bytes.

Actually, file-functions are oftentimes intended to have data be reinterpreted as/from something else. It allows you to have a struct like

typedef struct structSomeStruct
{
    char name[8]; // a basic, fixed length, string
    unsigned long i; // a 32 bit value
    float x, y, z, w;
} SomeStruct;

Or

class SomeStruct
{
    public:
        char name[8];
        unsigned long i;
        float x, y, z, w;

        SomeStruct()
        {
            // ...
        }
};

And store it to a file using something like:

SomeStruct st;

// populate the st data structure

// file.write(char* start_of_data, size_t number_of_bytes);
file.write(reinterpret_cast<char*>(&st), sizeof(SomeStruct));

And read it similarly.