10

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

4
  • 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) Commented Nov 15, 2013 at 7: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. Commented Nov 15, 2013 at 7: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. Commented Nov 15, 2013 at 7: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. Commented Nov 15, 2013 at 7:28

2 Answers 2

15

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)
Sign up to request clarification or add additional context in comments.

1 Comment

Note that even after 1-byte packing (which still must honor hardware alignment requirements), you're still platform dependent on endianess of multibyte scalers.
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.