This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

wrong evaluation of the compiler??... Very rare error

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

  • You just disovered the padding phenomenon! It's perfectly normal. Any book on the C programming language should have this topic covered.

  • Ok, sorry for my ignorance.. can you explain it to me and givem e a solution?

  • Yes - the normal solution is to let the compiler do what the compiler does.

    It's common that data types larger than one byte have a requirement on alignment. So two-byte integers must often be stored on even addresses.

    And the compiler must then also make sure that the full struct gets the same align as that internal element. So it doesn't matter if you have 16-bit + 8-bit fields - the compiler still needs to make the struct have same align as the 16-bit field. Hence if processor requires align for 16-bit field, then the full struct must have a size that is 2*n so that you can have an array of structs and each and every struct will start on a 2*n address.

    Most compilers have some option to pack structs, but that is costly. If the processor do need 16-bit align for a 16-bit number than a packed structure requires the compiler to produce code that picks up the field as two 8-bit reads and then combines them. Ugly and costly.

    So you normally do not want to play with packed data unless you really, really, really do need to. All covered on the net. Google has lots and lots of info about this. This forum too.

  • #pragma pack(1)

    This is the solution... don't know this.

    Thanksss!!!!!

  • Thanks for your explanation.

    I need a very short strcture to save in external I2C eeprom the maximum I can... can let the compiler to add more bytes.

    Thanks!!

  • Using __packed is probably better than using #pragma pack since __packed "sticks" to the type of the members but there are cases where the alignment info can get lost when using #pragma pack.

  • when donosaurs roamed the earth, memory was scarce and costly.

    today, memory is a commodity.

    if you decide to pack your structures they will take less memory (who cares) but have a speed penalty (you should care).

    sure, the above, as all generalization, will have exceptions.

    anyhow, a problem I ran into that deserves mentioning:

    If you have a structure that start with a char, the packings inserted may vary based on where ste struct end up being linked. This is a non-problem, unless you copy the struct.

    Erik

  • > if you decide to pack your structures they will take less memory
    > (who cares) but have a speed penalty (you should care).

    It depends. Many smartcards, intelligent sensors, etc. are using ARM
    processors these days and offer only some hundred byte of RAM, while
    the processing performance leaves enough headroom to justify less
    efficient memory access.

    > If you have a structure that start with a char, the packings
    > inserted may vary based on where ste struct end up being linked.

    I'd be surprised, since the location of struct members must be known
    at compile time.

    Kind regards,
    Marcus
    http://www.doulos.com/arm/

  • Some programs are code-space limited.
    Some are RAM-space limited.

    Most of the time, people don't reach any of the limits.

    Sometimes, the packed data get you into extra troubles because multiple memory accesses means a read of the value can't be performed atomically.

    So it isn't something to do unless there is a very good motive for it.

  • My post included:
    "sure, the above, as all generalization, will have exceptions."

    Which should have covered It depends

    this
    >
    > If you have a structure that start with a char, the packings
    > inserted may vary based on where ste struct end up being linked.

    I'd be surprised, since the location of struct members must be known
    at compile time.

    example
    struct
    U8 x
    U16 y

    if the linker happen to be at an odd address when it link the struct no packing will happen, if it is at an even address there will be a packbyte inserted between the two.

    I solved the problem this way
    struct
    U32 dummy
    U8 x
    U16 y

    Sometimes, the packed data get you into extra troubles because multiple memory accesses means a read of the value can't be performed atomically.
    I could see some processors burp on this. I do not know if any ARMs would, but a good argument for not packing

    Erik

  • 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.

  • #pragma pack(1)

    This is the solution.

    No, it is not "the" solution. It is what many people believe to be the only solution. Unfortunately, many of those people are wrong a good portion of the times. What these people ignore are the problem this so-called solution creates by itself.

    The central issue though is that the problem this is supposed to solve typically isn't actually a problem at all. And even if does happen to be an actual problem in a given case, packing the structure may easily create more problems than it solves.

  • If the linker happen to be at an odd address when it link the struct no packing will happen, if it is at an even address there will be a packbyte inserted between the two.

    No, it won't. It's forbidden by the language definition.

    A given struct will always have the exact same layout, no matter where it ends up: on the stack, on the heap, or in statically allocated memory --- of which the latter is the only one the linker is really even involved with in any way.

  • Can anybody help me with this???

    You could start by helping yourself in various ways.

    1) Try to take your own writing seriously, e.g. by finishing sentences with an appropriate amount of punctuation. That usually means one full stop, not half a dozen exclamation or question marks.

    2) Stop blaming your tools all that easily. You need to get to grips with the idea that lots of people have been using these tools intensely before you started, so when you thing you have a problem with them, it's quite extremely unlikely that the problem really is on the tools' end of things.

    3) Get some proper training, or at the very least a good textbook. You're developing a habit of jumping to conclusions based on incorrect beliefs. Wherever those beliefs come from, you need to replace that source of information by a better one.


  • Absolutely!!

    "Many people seem to adopt the jump-in-at-the-deep-end-and-hope-you-learn-to-swim-before-you-drown approach to start developing in 'C'"
    blog.antronics.co.uk/.../