anon
anon

Reputation:

Padded Structs (C++)

I have an 18 byte struct in C++. I want to read 18 bytes of data from a file straight into this struct. However, my C++ compiler pads the struct to be 20 bytes (4 byte aligned). This is relatively easy to get around for just my compiler alone but I would prefer to use a method that is more reliable cross-platform/cross-compiler.

This is the struct:

struct Test {
    uint8_t a;
    uint8_t b;
    uint8_t c;
    uint16_t d;
    uint16_t e;
    uint8_t f;
    uint16_t g; 
    uint16_t h; 
    uint16_t i; 
    uint16_t j; 
    uint8_t  k;  
    uint8_t  l;
};

I could add bytes to the front of the struct to guarantee it to be 32 bytes which would be a valid alignment on most systems, however I don't know if that would actually work with how structs need their elements to be naturally aligned.

Any help on this would be great but I could always end up copying the bytes manually into the attributes 😔.

Upvotes: 1

Views: 899

Answers (2)

Tomer W
Tomer W

Reputation: 3443

You have several options, and as usual, you should choose whatever best fits your needs:

  1. as stated before, don't read/write directly from/to memory, instead write each field separately (kind of how Java people would).
    This is the, I think, most portable, but WAY slower than the later methods.

  2. reorder the struct to match normal alignment (good practice anyway)
    in your example:

struct Test {
    uint8_t a;
    uint8_t b;
    uint8_t c;
    uint8_t f; // moved
    uint16_t d;
    uint16_t e;
//  uint8_t f;
    uint16_t g; 
    uint16_t h; 
    uint16_t i; 
    uint16_t j; 
    uint8_t  k;  
    uint8_t  l;
    uint16_t spare;
};

Note: it still have 2 byte padding, but not in the middle :)

  1. use #pragma pack(push, 1) on the struct to tell the compiler to NOT align the bytes

Note: you may need to place multiple #pragma to support different compilers

#pragma pack(push, 1)
struct Test {
    uint8_t a;
    uint8_t b;
    uint8_t c;
    uint16_t d;
    uint16_t e;
    uint8_t f;
    uint16_t g; 
    uint16_t h; 
    uint16_t i; 
    uint16_t j; 
    uint8_t  k;  
    uint8_t  l;
    uint16_t spare;
};
#pragma pack(pop)

I'd like to add that proper alignment helps the CPU process faster, therefore, you don't want to force pack = 1 on all structs... only those intended to be transmitted or received via communication channel.

Upvotes: 1

Dúthomhas
Dúthomhas

Reputation: 10113

If you are writing for cross-platform and cross-compiler, forget the direct read. Just read byte-by-byte. If profiling indicates you need further optimization, either play with the underlying stream buffering or read into a byte array and extract each piece.

This also eliminates system endianness problems.

// extract byte values
t.a = f.get();
t.b = f.get();

...

// extract a little-endian value
t.d = f.get();
t.d = t.d | (f.get() << 8);

...

// check for success or failure
if (!f) ...

Block read via a byte array:

unsigned char buffer[ 18 ];
if (!f.read( (char *)buffer, sizeof(buffer) ))
  ...

t.a = buffer[0];
...
t.d = buffer[3] | (buffer[4] << 8);
...

Again, profile before deciding there is an issue.

Upvotes: 1

Related Questions