The term “64-bit machine” is vague. Computers processors and systems have several features which may have sizes different from each other on the same machine, including:
- The width of most processor registers.
- The width of addresses.
- The width of the data bus.
- The width of the arithmetic logic units.
For the moment, let’s assume all of these are 64 bits. Even so, why would we require, say uint32_t to be aligned to 64 bits?
One reason to require alignment is to avoid splitting accesses across memory transfers. If the bus is 64 bits wide, the system is generally designed to access memory at multiples of eight bytes (64 bits). When the processor wants to read some memory, say from a 64-bit address, it sends only the first 61 bits to the memory device(s). (61 is a lot, but we have assumed everything is 64 bits in this machine.) The memory device(s) get all eight bytes matching those 61 bits—all eight combinations of the low three bits that we did not send. It gets eight bytes at a time because that is what fits on the bus, and we want to be efficient.
So, whenever the process reads from memory, it is always going to get eight bytes, and those bytes are going to be 64-bit aligned.
Now we can see that if a uint32_t started at some address, say xxx0101, where the x’s represent bits we do not care about, its four bytes would be at addresses xxx0101, xxx0110, xxx0111, and xxx1000. But that fourth byte is in a different group of eight. The first three are all in the same group, the one addressed by the initial bits xxx0. The last byte is in a new group, xxx1. To read this uint32_t, we have to do two reads from memory. That is inefficient.
However, if the uint32_t is at either address xxx0000 or xxx1000, its bytes are all within one group. They might be the first four or the last four bytes in that group, so we will need the processor to be able to select the first four or the last four from the eight it gets from memory, but it will take only one read from memory to get the bytes.
Therefore, four-byte alignment for uint32_t suffices to ensure it is well enough aligned that we only need one read to get it from memory.
There is very little reason to require eight-byte alignment. One reason could be that, if it were eight-byte aligned, we would not need the extra wires and switches in the processor to be able to select the first four or the last four bytes out of eight. We would only need to take the first four. But this slight advantage is hugely overwhelmed by the fact it would mean we can store only one uint32_t in every eight bytes. Half the memory would be wasted with padding. With four-byte alignment, we can read uint32_t objects pretty well, and we can read two at a time.
With uint8_t, eight-byte alignment would be even worse, we could have only one uint8_t in every eight bytes, wasting 87.5% of the memory.
For the most part, an object of length n bytes only needs to have an n-byte alignment in order to perform well with the hardware (supposing n is a power of two). That alignment will allow them to fit neatly into the bus and memory operations, whatever width they are.
Further, if the bus width is b and the object size is n, the alignment requirement may just be the lesser of b or n. Once an object is larger than the bus width, we are going to need multiple transfers to get it, and often nothing is gained by requiring more alignment than the bus width.