0
votes

I am making a GUI that send/receives data over a serial port. The data consists of messages that are defined in a struct like this:

typedef struct
{
    uint8_t a;
    uint8_t b;
    uint8_t c;
    uint16_t d[3];
    uint16_t e;
} MyMsg_t;

I am also using a union, because it makes it easier for me to be able to set the data fields and be able to send it byte for byte. The union looks like this:

typedef union
{
    MyMsg_t msg;
    uint8_t array[MyMsgLength];
} MyMsg;

I now try to add some data to a message like this:

MyMsg msg;

msg.msg.a    = (uint8_t) 1;
msg.msg.b    = (uint8_t) 2;
msg.msg.c    = (uint8_t) 3;
msg.msg.d[0] = (uint16_t) 4;
msg.msg.d[1] = (uint16_t) 5;
msg.msg.d[2] = (uint16_t) 6;
msg.msg.e    = (uint16_t) 7;

And I transmit it over a serial bus byte-wise and the receiving end is:

1 2 3 19 4 0 5 0 6 0 7

(the data in c through e is reversed because of the bus)

This looks like the struct actually was:

typedef struct
{
    uint8_t a; //1
    uint8_t b; //2
    uint8_t c; //3
    //uint8_t x //19
    uint16_t d[3];
    uint16_t e;
} MyMsg_t;

From this I can assume that it somewhere in the C standard says that the struct must be minimum n * sizeof(uint16_t) in this case as we can not have for example 3.5 of the largest type in a struct, but it has to be an integer?

I guess this is what is called padding? Is there some way to force a struct to be n * sizeof(uint8_t) even when a larger type is present?

I know how I can avoid this, but it requires not using the union and more code. Is there some way to elegantly avoid this issue with minimal code intervention?

1
The standard doesn't guarantee it one way or another but permits padding. The reason is efficiency because properly aligned data is faster to read and write. Your compiler should have a pragma pack or the like (compiler option?) which tells it to not pad, exactly for cases when the struct layout must match a hardware. - Peter - Reinstate Monica
Using a struct in this way is inherently non-portable. It may seem "inelegant" at first to serialize your data in a portable way, but it is fewer headaches in the long run. - M.M
@MattMcNabb: I get your point and I agree. I removed the union and dont access the struct data fields indirectly as above. - uniquenamehere

1 Answers

2
votes

Don't forget about the endianness if the machine too, which is why doing this is not a recommended practice. If you don't care about endianness or are dealing with it some other way, then this approach is acceptable.

You can change the alignment to eliminate padding. How this is done depends on the compiler, however:

Other compilers may have other options or means of accomplishing the same thing.