9

I have the problem of casting a byte array to a struct, some bytes are ignored or skipped.

Given the following struct,

typedef struct
{
    uint32_t id;
    uint16_t test;
    uint8_t group;
    uint32_t time;
    uint16_t duration;
    uint8_t a;
    uint8_t b;
    uint8_t c;
    uint16_t d;
    uint16_t e;
    uint8_t status;
    uint8_t x;
    uint8_t y;

} testStruct_t, *PtestStruct_t;

I have an array with the following test data:

uint8_t pBuff = { 0x11 , 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 };

The casting is done as follows:

PtestStruct_t pStruct = (PtestStruct_t)pBuff;

Somewhere in the structure some bytes are skipped or ignored. I do not know why. This has been tested in Visual Studio 2012 and on a ARM processor on which this testing and debugging was made required.

What am I missing here? I do not believe it is Endian related. It could be the compiler in both test cases, I do not know what to do in this last case.

The bytes which are being skipped/ignored are 0x88 and 0x14

Armandt
  • 311
  • 1
  • 3
  • 8
  • 3
    maaping struct to binary format can't be done without care. You should read about `padding` and `alignment` and (the bad news, it's compiler / processor specific) – Bruce Nov 15 '13 at 07:12
  • 1
    If you're doing this as-shown here, its already at best a platform and implementation-dependant solution, and likely wrong anyway (as you're discovering). Both implementation dependent padding and alignment will effect the outcome you're looking for. – WhozCraig Nov 15 '13 at 07:15
  • there is usually a compiler directive or #pragma to specify structure padding and alignment. #pragma pack(0) or the gcc option -fpack-struct[=n] are a couple of ways of controlling this. Visual Studio also has an option in the GUI to set the structure padding for your project. – Mark Hendrickson Nov 15 '13 at 07:18
  • 1
    The bug itself is not endian-related, but your code relies on endianess and is non-portable. That happens as soon as you try to access the individual bytes of an integer type which is 16 bytes or larger. Endian-independent code does not rely on casts etc, but instead accesses individual bytes through bit shifting and bit masking. `(my_uint32 >> 24) & 0xFF` is for example guaranteed to hold the most significant byte of a number, no matter endianess. – Lundin Nov 15 '13 at 07:28

2 Answers2

14

You're encountering alignment padding.

uint32_t id;     // offset 0
uint16_t test;   // offset 4
uint8_t group;   // offset 6
uint32_t time;   // offset 7

The offsets shown here are likely to be wrong. The compiler will probably place padding between "group" and "time" to ensure that "time" is on a 4-byte boundary (the actual alignment is configurable)

If you absolutely require the structure to be like that, you can use #pragma pack

#pragma pack(push, 1)
typedef struct
{
    uint32_t id;
    uint16_t test;
    uint8_t group;
    uint32_t time;
    uint16_t duration;
    uint8_t a;
    uint8_t b;
    uint8_t c;
    uint16_t d;
    uint16_t e;
    uint8_t status;
    uint8_t x;
    uint8_t y;

} testStruct_t, *PtestStruct_t;
#pragma pack(pop)
kfsone
  • 23,617
  • 2
  • 42
  • 74
  • 10
    Note that even after 1-byte packing (which still *must* honor hardware alignment requirements), you're still platform dependent on endianess of multibyte scalers. – WhozCraig Nov 15 '13 at 07:20
4

Compiler may have added some byte between your struct fields for alignment. You need to use a packing to prevents compiler from doing padding - this has to be explicitly requested - under GCC it's attribute((packed)),

example:

      struct __attribute__((__packed__)) mystruct_A {
      char a;
      int b;
     char c;
     };

and for Visual Studio consult MSDN

alexbuisson
  • 7,699
  • 3
  • 31
  • 44