0

I'm trying to copy a byte array to a struct:

Actual bytes:

00000000 | ff 53 4d 42 72 00 00 00 00 08 01 c8 00 00 00 00 | .SMBr...........

Destination structure:

typedef struct {
    uint8_t protocol[4];
    uint8_t command;
    uint32_t status;
    uint8_t flags;
    uint16_t flags2;
    uint16_t pidHigh;
    uint16_t somethingElse;
} MyStruct;

But for some reason, bytes in myStruct.status are not what they're supposed to be:

printf("%x", bytes[4]);
  => 72          // Ok

printf("%x", myStruct.command);
  => 72          // Ok

printf("%02x%02x%02x%02x", bytes[5], bytes[6], bytes[7], bytes[8]);
  => 00000000    // Ok

printf("%"PRIX32, myStruct.status);
  => C8010800    // What?! Why did it take the next 4 bytes... and reversed them?

Code used to copy those bytes:

MyStruct myStruct; 
memcpy(&myStruct, bytes, 16);

This code is running on ARM (iPhone 5), which might explain the little-endianness of the output, but it doesn't explain why there's a +4 bytes offset in the bytes that've been copied.

What's going on here?

ldiqual
  • 15,015
  • 6
  • 52
  • 90
  • 1
    You should take the address of the various struct elements and compare them to the address of the beginning of the struct - you may be surprised by what you find. The compiler has a lot of freedom to add padding in between various struct elements. Certain ARM architectures are also quite strict about words being aligned to word-boundaries in memory – Pete Baughman Apr 25 '14 at 01:18
  • 3
    `struct` alignment and padding strikes again. – Deduplicator Apr 25 '14 at 01:20
  • 1
    After you try that, take a look at http://stackoverflow.com/questions/119123 – aschepler Apr 25 '14 at 01:20

2 Answers2

2

The memory layout of a struct is going to conform to the alignment requirements of its members. On 32-bit ARM, 16-bit values need 2 byte alignment and 32-bit and greater values require 4 byte alignment. There are padding bytes in between the structure elements when the alignment doesn't match from one to another. Due to this padding, copying or casting arrays of bytes to a struct is not going to work as you expect.

Unfortunately, there is no great way around this. You can choose to pack your structures, which may reduce their performance. You can copy each element individually. Or you can carefully arrange your structures so that they are tightly packed (assuming you are aware of the alignment rules for all platforms your code will run on).

For example: if you rearrange you struct in this way, it will be perfectly packed with no padding bytes in the middle or at the end (it is an even multiple of 4).

typedef struct {
    uint32_t status;           // +0
    uint16_t flags2;           // +4
    uint16_t pidHigh;          // +6
    uint16_t somethingElse;    // +8
    uint8_t command;           // +10
    uint8_t flags;             // +11
    uint8_t protocol[4];       // +12
} MyStruct;
Variable Length Coder
  • 7,958
  • 2
  • 25
  • 29
0

The compiler aligns the elements in the struct, so that all of them occupy a space in memory equal to a multiple of 4.

So basically, command, that supposedly uses 1 byte only, is followed by 3 bytes of garbage before status.

You can tell the compiler to not do that by setting this:

#pragma pack(1)
Havenard
  • 27,022
  • 5
  • 36
  • 62
  • I get it, thanks! However, isn't it weird that this garbage is exactly the 4 bytes following `status`... and reversed? – ldiqual Apr 25 '14 at 01:23
  • The code in your post doesn't print any of the padding bytes. – aschepler Apr 25 '14 at 01:25
  • It has something to do with performance and how the CPU fetch data from memory. [This other question here](http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member) seems to explain better why it happens. – Havenard Apr 25 '14 at 01:26