Question

I've read that reinterpret_cast<> can be dangerous if not used properly. So I believe that i was using it properly ;). I found it's good to use if I've templates classes and type conversion is required on that. But recently I've read that reinterpret_cast<> is non-portable too. I'm sad for this point. What's the reason? Take the following code,

void Disp(int* val)
{
    for (int i=0; i < SZ; ++i)
    {
        cout << *(val+i) << " ";
    }
    cout << endl;
}

int main()
{
    int arr[SZ];
    Disp(arr);

    unsigned char* ptr = reinterpret_cast<unsigned char*>(arr);
    for (unsigned char* i = ptr; i < (ptr + (SZ * sizeof(int))); i++)
    {
        *i = 0;
    }
    Disp(arr);
    return 0;
}

Now the outputs:

1174214872 32767 4196789 0 568392584 58 4196720 0 0 0 
0 0 0 0 0 0 0 0 0 0 

Machine type: Linux 2.6.32-358.11.1.el6.x86_64 #1 x86_64 x86_64 x86_64 GNU/Linux

975580 -16506540 -13369152 0 -4202936 67876 3 -4202836 4 -4202828 
0 0 0 0 0 0 0 0 0 0 

Machine type: SunOS DELPHI 5.10 Generic_142900-01 sun4u sparc SUNW,Netra-240

I've copied the outputs of the same program, both in Linux and Solaris. I'm new to portability issues. So can anyone please tell me, if I'm using something like this in my code, will that cause any portability issues? Even if not with this code, is there a chance for surprises (undefined behavior) when the code get complex (with dynamic allocation and all) and running for long hours. Thanks for the help.

Was it helpful?

Solution

The portability issue with reinterpret_cast<> lies in the fact that different CPUs store numbers differently in memory. Some store them from the least significant byte up to the most significant one (little endian), others do it precisely the other way around (big endian). Some even use some weird byte order like 1 0 3 2, don't ask me why.

Anyway, the consequence of this is, that reinterpret_cast<> is portable as long as you do not rely on the byte order in any way.

Your example code does not rely on byte order, it treats all bytes the same (setting them to zero), so that code is portable. If you would use a reinterpret_cast<> to copy some data object on the same machine without interpreting the bytes, the code would also be portable (memcpy() does this).

What is not portable is stuff like taking a look at the first byte to determine the sign of a number (works only on big endian machines). If you try to transfer data from one machine to another by just sending the result of reinterpret_cast<char*>, you are also in trouble: the target machine may use a different byte order than the source machine, completely misinterpreting your data.

I would say that it is wrong to say that the reinterpret_cast<> is non-portable, it simply exposes a machine detail to the C++ code which is machine-specific. And any code that relies on that machine detail is non-portable.

OTHER TIPS

There are some problems with this code.

  • You're displaying the contents of an uninitialized array. There's really no point to it.
  • You're hard coding the size of your loop, assuming sizeof(int) == 4. If you are on a machine with a different size int, this will be non-portable.
  • If you were filling memory with any value other than zero, you'd need to worry about the endianness of the processor.

Edit: I see you've edited out the hard-coded magic constant 4 so point 2 no longer applies.

I think this is OK. I have seen someone argue that it is undefined because the list of allowed aliases in [basic.lval]#10 does not include using an int to aliasing values written via a char type . (The opposite of that is included, however).

However, I interpret "stored value of an object" to still refer to the int value, even after being written via the char alias. This does not seem to be clearly expressed in the C++ standard; but in C99 (which these rules are clearly derived from), it has different wording involving effective types which specifies that the effective type of the memory is still int despite it being written via the char alias.

To clarify; in both languages, there are other rules which cover the issue of whether or not a trap representation is created. But I have seen it argued that the strict aliasing rule in C++ trumps those rules for this case.

The issue with the portability/utility of reinterpret_cast<> will be very specific to how it's being used. Getting answers about a toy example wont help with whatever case you might actually find yourself wanting to use it.

generally speaking, if there's a way to do something without using reinterpret_cast<>, then you should. Using the example in your question, you can portably clear the array to zero without using reinterpret_cast<> by using an int* (even though I think that your example's used ofreinterpret_cast<>is in fact well-defined and portable - using a pointer that has been reinterpret-casted to achar*` is one of the few areas that can be portable).

In most cases where you might find that you need reinterpret_cast<>, you are dealing with something that really isn't portable and you should understand why you need reinterpret_cast<> in that specific case and understand the portability issues that will be specific to that particular case.

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