I am currently teaching myself c and learning all I can about memory. I found out that you can use a char pointer to copy the bit pattern of an int for example and store it in memory with casting:
#include <iostream>
using namespace std;
int main()
{
int x = 20;
char* cp = new char[sizeof(int)];
cp[0] = *((char*)&x);
cp[1] = *((char*)&x 1);
cp[2] = *((char*)&x 2);
cp[3] = *((char*)&x 3);
std::cout << (int)*cp; // returns 20;
return 0;
}
the code above works, when I cast cp to a int so the compiler reads 4 bytes at a time, I get the correct number which is 20.
However changing it to a float:
#include <iostream>
using namespace std;
int main()
{
float x = 20;
char* cp = new char[sizeof(float)];
cp[0] = *((char*)&x);
cp[1] = *((char*)&x 1);
cp[2] = *((char*)&x 2);
cp[3] = *((char*)&x 3);
std::cout << (float)*cp; // returns 0.
return 0;
}
returns 0. Now I am a bit confused here. If I am copying every single byte, why is it still giving me a 0? If someone could help me out understanding this it would be very awesome.
CodePudding user response:
(int)*cp;
first dereferences the pointer, returning a char
value, that is now static-casted to integers. This will only work for the range char
can store - 0 255
or -128 127
and requires a little-endian system.
It may seem that the way how to fix it would be *reinterpret_cast<float*>(cp);
or *((float*)cp)
. Both are wrong and cause undefined behaviour because they break the strict aliasing rule.
The strict aliasing rule states that one can dereference a pointer to T
only if there exists an object of type T
at the memory location the pointer points to. With exception of char
, std::byte
, unsigned char
. Meaning it is correct to inspect any type through cast to char
, but one cannot simply interpret bunch of bytes as a random T
.
The correct way to serialize and deserialize objects is:
#include <iostream>
using namespace std;
int main() {
float x = 20.0f;
// This is safe.
char* cp1 = reinterpret_cast<char*>(&x);
// Also safe because there is a float object at cp1.
std::cout << *reinterpret_cast<float*>(cp1);
// No need for dynamic allocation.
char cp2[sizeof(float)];
// Copy the individual bytes into a buffer.
// = Serialize the object.
std::memcpy(cp2, &x, sizeof(x));
// NOT SAFE, UNDEFINED BEHAVIOUR
// There is no float object at cp2.
std::cout << *reinterpret_cast<float*>(cp2);
// Deserialization through copy
float y;
std::memcpy(&y, cp2, sizeof(y));
// Safe
std::cout << y;
return 0;
}