Dear all,
I have this ultra simple strcture
typedef struct { uint8_t Hora; uint16_t Data1; } EEpromPaqueteDatosS;
When I do sizeof(EEpromPaqueteDatosS) it returns 4!!! But if I do the sizeof of the same structure with only one variable, in other words, with uint8_t variable it returns 1 and with uint16_t it returns 2.. but if the structure have the two variables it returns 4!!
Also if I copy the structure to a uint8_t vector I can found a strange byte in the middle.. like this:
EEpromPaqueteDatosS EEpromPaqueteDatos; uint8 data[4];
EEpromPaqueteDatos.Hora = 0x10; EEpromPaqueteDatos.Data1= 0x1020;
When I copy the structure to data, data is like 0x10, 0x??, 0x20, 0x10.. why this extra byte!!!!
Can anybody help me with this???
Thanks
Basically, processors who can read "odd-aligned" data can do it because they have an intelligent memory controller that puts the processor core to sleep while the memory controller performs multiple memory accesses and then merges the relevant bytes.
The memory controllers inside Intel x86 chips have a tradition of silently hiding odd aligns. So x86 programs have by tradition always managed, while same source code recompiled on Sun or other platforms have resulted in "bus error" exceptions when the memory controller have received an unsupported request.
For processors with this feature in the memory controller interface, you have compiler options to specify align just for performance. Even if the memory interface is pipelined, you still increase the probability of a pipeline stall when the memory controller must perform two sequential accesses.
For processors where the memory controller can not perform this merge, the compiler really must insert code for multiple reads/writes. A 4-byte integer one byte off can requires a read/modify/write to fix that odd byte and a read/modify/write to fix the other three bytes. Few processors have a lock primitive where the processor can lock the bus (dma or possibly external memory accesses by other devices) and/or lock interrupts from interfere with these two read/modify/write actions.
About structs and linking - a "normal" compiler/linker, i.e. a compiler/linker that are following the C language standard, would not do any three-byte optimization of a struct just because the linker has the option to place the struct at an odd or even location. If the struct has an alignment critieria larger than 1, then the compiler has to make the struct n times that alignment criteria. And make the relevant fields inside the struct placed so they fulfill that requirement. And the linker must place the full struct so the struct is aligned to this - or better - alignment level.
So there isn't a difference between:
struct { uint8_t a; uint16_t b; } alt_1; struct { uint16_t a; uint8_t b; } alt_2;
If processor needs to have uint16_t aligned to even address, then alt_1 must be n*2 bytes large with field b at an even offset inside the struct. And alt_2 must be n*2 bytes large with a at an even offset inside the struct.
So sizeof(alt_1) and sizeof(alt_2) will both be n*2 bytes large. And a memcpy(&alt_1,...) or memcpy(&alt_2,...) will be able to perform a memory copy without reacing any byte outside of the struct.
And it will be possible to create arrays of above types and ever element in that array will start at a n*2 byte address since each element will be n*2 bytes large.
Note that the C51 compiler don't need to worry about align from a processor perspective since the 8051 performs 8-bit memory accesses. So it's ok for the compiler to use zero padding and make above structures 3 bytes large. And the linker will be free to place variables of these types at odd or even addresses without needing to worry.
The memory controllers in most ARM chips will not like the above as 3 byte structures, so the compiler will either add a pad or add a number of extra instructions to make only aligned accesses.