Question

According to Effective C++, "casting object addresses to char* pointers and then using pointer arithemetic on them almost always yields undefined behavior."

Is this true for plain-old-data? for example in this template function I wrote long ago to print the bits of an object. It works splendidly on x86, but... is it portable?

#include <iostream>

template< typename TYPE >
void PrintBits( TYPE data ) {
    unsigned char *c = reinterpret_cast<unsigned char *>(&data);

    std::size_t i = sizeof(data);
    std::size_t b;

    while ( i>0 ) {
        i--;
        b=8;
        while ( b > 0 ) {
            b--;
            std::cout << ( ( c[i] & (1<<b) ) ? '1' : '0' );
        }
    }
    std::cout << "\n";
}

int main ( void ) {
    unsigned int f = 0xf0f0f0f0;
    PrintBits<unsigned int>( f );
    return 0;
}
Was it helpful?

Solution

It certainly is not portable. Even if you stick to fundamental types, there is endianness and there is sizeof, so your function will print different results on big-endian machines, or on machines where sizeof(int) is 16 or 64. Another issue is that not all PODs are fundamental types, structs may be POD, too.

POD struct members may have internal paddings according to the implementation-defined alignment rules. So if you pass this POD struct:

struct PaddedPOD
{
char c;
int i;
}

your code would print the contents of padding, too. And that padding will be different even on the same compiler with different pragmas and options.

On the other side, maybe it's just what you wanted.

So, it's not portable, but it's not UB. There are some standard guarantees: you can copy PODs to and from array of char or unsigned char, and the result of this copying via char buffer will hold the original value. That implies that you can safely traverse that array, so your function is safe. But nobody guarantees that this array (or object representation) of objects with same type and value will be the same on different computers.

BTW, I couldn't find that passage in Effective C++. Would you quote it, pls? I could say, if a part of your code already contains lots of #ifdef thiscompilerversion, sometimes it makes sense to go all-nonstandard and use some hacks that lead to undefined behavior, but work as intended on this compiler version with this pragmas and options. In that sense, yes, casting to char * often leads to UB.

OTHER TIPS

Yes, POD types can always be treated as an array of chars, of size sizeof (TYPE). POD types are just like the corresponding C types (that's what makes them "plain, old"). Since C doesn't have function overloading, writing "generic" functions to do things like write them to files or network streams depends on the ability to access them as char arrays.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top