Search code examples
c++pointersfstreamreinterpret-cast

Using a pointer for casting char* to unsigned char*


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.


Solution

  • 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.