ᴘᴀɴᴀʏɪᴏᴛɪs
ᴘᴀɴᴀʏɪᴏᴛɪs

Reputation: 7519

Invalid sizeof() struct, gap between members

I have a struct like this:

typedef struct _HEADER_IO
    {
        uint8_t field1 : 2;
        uint8_t field2 : 4;
        uint8_t field3 : 1;
        uint8_t field4 : 1;
        uint16_t field5;
        uint8_t field6;
    } HEADER_IO;

It's basicly a message header that will be sent over tcp. The server reads this so that it knows what data follows in the buffer. However for some reason intead of the size being 4 bytes (2+4+1+1 first byte + 2 bytes from field 5 + 1 byte field 6) the size is 6 bytes.

Looking it up in memory view it is:

XX AA XX XX XX AA

Instead of:

XX XX XX XX

Where AA are never set no matter what I do. This is a problem because I am planning for the header to be send() to a server and the extra bytes are included making the server interpret the header wrong. What am I doing wrong?

Upvotes: 0

Views: 303

Answers (2)

unwind
unwind

Reputation: 399803

In general, it's a bad idea to use bitfields for things like these. Since you can't know beforehand exactly which byte the bits will end up in, and since there are padding and alignment issues.

In my opinion, it's better to "own up" to the fact that you need more control over the external representation than what C structures give you, and do it manually. You can of course keep the struct as the in-memory (internal) representation.

Basically, you would write a function like:

size_t header_serialize(unsigned char *buf, size_t max, const HEADER_IO *header);

whose job it would be to, in the memory at buf, build the proper byte sequence that represents header.

To clarify (based on comments), the intent is to read the fields from header, not just do e.g.

memcpy(buf, header, sizeof *header);  /* DON'T DO THIS! */

Instead, you're supposed to assemble the expected external representation, byte by byte, from the fields of header. That way, you always get the same external representation regardless of what the compiler does to the in-memory format of header.

Upvotes: 1

jjrv
jjrv

Reputation: 4345

In standard C you can't help the fact that struct members can have padding inserted between them. You have to write a function to decode the data and store it in your struct before processing. This is because on some architectures unaligned memory access (reading from a pointer not aligned to, for example, 4 bytes) is very expensive and C will automatically pad your structures to avoid the cost. There's no standard way to turn the feature on or off.

For example in GCC you can add __attribute__((packed)) after the struct definition and Visual Studio has some #pragma commands (see http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html) that are also supported by GCC but beware that overall this is non-standard.

Since your comments mentioned it's a Windows program, probably it would work if you add this before the struct definition:

#pragma pack(push,1)

And this after it:

#pragma pack(pop)

While it would be more portable to write code to more manually decode the header, the above approach should be faster.

Upvotes: 0

Related Questions