We can look at the representation of an object of type T
by converting a T*
that points at that object into a char*
. At least in practice:
int x = 511; unsigned char* cp = (unsigned char*)&x; std::cout << std::hex << std::setfill('0'); for (int i = 0; i < sizeof(int); i++) { std::cout << std::setw(2) << (int)cp[i] << ' '; }
This outputs the representation of 511
on my system: ff 01 00 00
.
There is (surely) some implementation defined behaviour occurring here. Which of the casts is allowing me to convert an int*
to an unsigned char*
and which conversions does that cast entail? Am I invoking undefined behaviour as soon as I cast? Can I cast any T*
type like this? What can I rely on when doing this?
You can have pointers to pointers: char **s2 = &s; s2 just stores the address of the variable s . Pointers to pointers come up when you're dealing with arrays of pointers, or when you're passing a pointer to a function and you want the function to be able to write a new pointer value.
The one advantage that I'm seeing is that with char * it is easier to distinguish "no string provided" and "empty string provided", also it occupies less space then an array. Also, in both cases I can initialize: struct my_struct p { . p = 10, .
Size of a pointer is fixed for a compiler. All pointer types take same number of bytes for a compiler. That is why we get 4 for both ptri and ptrc.
Which of the casts is allowing me to convert an
int*
to anunsigned char*
?
That C-style cast in this case is the same as reinterpret_cast<unsigned char*>
.
Can I cast any T* type like this?
Yes and no. The yes part: You can safely cast any pointer type to a char*
or unsigned char*
(with the appropriate const
and/or volatile
qualifiers). The result is implementation-defined, but it is legal.
The no part: The standard explicitly allows char*
and unsigned char*
as the target type. However, you cannot (for example) safely cast a double*
to an int*
. Do this and you've crossed the boundary from implementation-defined behavior to undefined behavior. It violates the strict aliasing rule.
Your cast maps to:
unsigned char* cp = reinterpret_cast<unsigned char*>(&x);
The underlying representation of an int
is implementation defined, and viewing it as characters allows you to examine that. In your case, it is 32-bit little endian.
There is nothing special here -- this method of examining the internal representation is valid for any data type.
C++03 5.2.10.7: A pointer to an object can be explicitly converted to a pointer to an object of different type. Except that converting an rvalue of type "pointer to T1" to the type "pointer to T2" (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.
This suggests that the cast results in unspecified behavior. But pragmatically speaking, casting from any pointer type to char*
will always allow you to examine (and modify) the internal representation of the referenced object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With