Yes, that could cause problems.
4-alignment simply means that the pointer, when considered as a numeric address, is a multiple of 4. If the pointer is not a multiple of the required alignment, then it is unaligned. There are two reasons why compilers place alignment restrictions on certain types:
- Because the hardware cannot load that datatype from an unaligned pointer (at least, not using the instructions which the compiler wants to emit for loads and stores).
- Because the hardware loads that datatype more quickly from aligned pointers.
If you're in case (1), and double is 4-aligned, and you try your code with a char *
pointer which is not 4-aligned, then you'll most likely get a hardware trap. Some hardware does not trap. It just loads a nonsense value and continues. However, the C++ standard doesn't define what can happen (undefined behavior), so this code could set your computer on fire.
On x86, you're never in case (1), because the standard load instructions can handle unaligned pointers. On ARM, there are no unaligned loads, and if you attempt one then your program crashes (if you're lucky. Some ARMs silently fail).
Coming back to your example, the question is why you're trying this with a char *
that isn't 4-aligned. If you successfully wrote a double there via a double *
, then you'll be able to read it back. So if you originally had a "proper" pointer to double, which you cast to char *
and you're now casting back, you don't have to worry about alignment.
But you said arbitrary char *
, so I guess that's not what you have. If you read a chunk of data out of a file, which contains a serialized double, then you must ensure that that the alignment requirements for your platform are met in order to do this cast. If you have 8 bytes representing a double in some file format, then you cannot just read it willy-nilly into a char* buffer at any offset and then cast to double *
.
The easiest way to do this is to make sure that you read the file data into a suitable struct. You're also helped by the fact that memory allocations are always aligned to the maximum alignment requirement of any type they're big enough to contain. So if you allocate a buffer big enough to contain a double, then the start of that buffer has whatever alignment is required by double. So then you can read the 8 bytes representing the double into the start of the buffer, cast (or use a union) and read the double out.
Alternatively, you could do something like this:
double readUnalignedDouble(char *un_ptr) {
double d;
// either of these
std::memcpy(&d, un_ptr, sizeof(d));
std::copy(un_ptr, un_ptr + sizeof(d), reinterpret_cast<char *>(&d));
return d;
}
This is guaranteed to be valid (assuming un_ptr really points to the bytes of a valid double representation for your platform), because double is POD and hence can be copied byte-by-byte. It may not be the fastest solution, if you have a lot of doubles to load.
If you are reading from a file, there's actually a bit more to it than that if you're worried about platforms with non-IEEE double representations, or with 9 bit bytes, or some other unusual properties, where there might be non-value bits in the stored representation of a double. But you didn't actually ask about files, I just made it up as an example, and in any case those platforms are much rarer than the issue you're asking about, which is for double to have an alignment requirement.
Finally, nothing at all to do with alignment, you also have strict aliasing to worry about if you got that char *
via a cast from a pointer which is not alias-compatible with double *
. Aliasing is valid between char *
itself and anything else, though.