Juliean said:
Puh, I'm not an expert on the c++-standard, but from the wording I've read (can't find it quickly right now) I always assumed that even this was illegal:
I'd have to look it up. I think the cast is legal as long as pMemory
“is an int”, that is, in both C and C++ these are allowed
// 1. Round trip some type through signed/unsigned char* or void* and back again
int_ptr2 = (int*)(char*)int_ptr;
foo->user_ptr = (void*)&my_int; // or often a larger structure
int fd = *(int*)foo->user_ptr; // generally in a callback or such later
// 2. Take part of a char* buffer and treat it as some *single* type (basically a memory allocator)
char *memory = ...;
// can be any subset of memory, and might be via void*, but we must ensure correct alignment for the T* being made!
// malloc and friends I believe are speced to return a pointer aligned to all primitives types, but on some platforms this might still not be enough for some vector types outside the C/C++ standards
int *int_ptr = (int*)(memory + 20);
*int_ptr = 20;
int y = *int_ptr + 5;
// On C++, you have placement new + delete, and is required for anything with a constructor or destructor!
// Still have to ensure alingment!
std::string *str = new(memory + 24) std::string("Hello world!");
str->~string(); // when done with it, does not deallocate "memory" but that block could now be used for another object safely
The bit I am not sure on in 2. is if this is legal, which would have to check
char *memory = ...;
int *int_ptr = (int*)(memory + 20);
*int_ptr = 5; // "allocated" memory
// later
int *int_ptr2 = (int*)(memory + 20); // Note I casted again, instead of using the same int_ptr as before
int x = *int_ptr2; // The compiler can see the cast and might consider this memory as not an integer and be UB
But this is also not exactly the same as what memcpy
does, since it never casts the pointer to an incompatible type (you are only doing T* → char/void*, never char/void* → T*), it simply copies bytes.
void memcpy(void *dst, const void *src, size_t len) // I beleive char* is equally valid
{
char *dst2 = (char*)dst; // casting any pointer to char* is OK
const char *src2 = (const char *)src;
for (size_t i = 0; i < len; ++i)
dst2[i] = src2[i]; // copying the char values is always legal
}
Also if I recall, copying the bytes from say an integer to a float or partial copies between different sizes is “implementation defined” rather than “undefined”. This is because the standard does not promise what the byte level format of these types is, and so it can't tell you what specific bit/byte values will be.
I think the newer standard promises twos-complement signed integers so copying between signed and unsigned integers of the same size is defined. But the floating point formats are still implementation specific, as is the sizes of char/short/int/long/float/double/etc. within certain constraints.
You will also encounter a similar thing with binary file or network IO.