Reputation: 51
How we can avoid structure padding in C apart from using pragma pack aur bit field? Is there any other method available?
Upvotes: 5
Views: 4606
Reputation: 155610
The only completely portable and reliable way to avoid structure padding is not to use real members in your struct
at all. Use a single char
array member, and define macros that access its contents:
struct paddingless {
// char *p;
// double d;
// char c;
#define OFFSET_P 0
#define OFFSET_D (OFFSET_P + sizeof(char *))
#define OFFSET_C (OFFSET_D + sizeof(double))
#define OFFSET_END (OFFSET_C + sizeof(char))
char data[OFFSET_END];
};
Portable getters and setters would look like this:
inline double paddingless_get_d(const struct paddingless *o) {
double val;
memcpy(&val, o->data + OFFSET_D, sizeof(val));
return val;
}
inline void paddingless_set_d(struct paddingless *o, double val) {
memcpy(o->data + OFFSET_D, &val, sizeof(val));
}
If you know that your architecture accepts unaligned access, you can get away with a setter being defined with a cast:
#define paddingless_get_d(o) (*(double *) ((o)->data + OFFSET_D))
#define paddingless_set_d(o, val) (*(double *) ((o)->data + OFFSET_D) = (val))
This is non-portable, but potentially faster than the alternative. And it works on a vax x86...
Upvotes: 3
Reputation: 215305
There are some things to consider. First of all, the compiler adds padding for a reason: it tries to make the optimal, working machine code for its designated platform. So normally padding is a good thing. Except for the cases where you want to define data communications protocols, hardware register mappings and similar, where the number of bytes simply must match a certain specification.
There is really no standard way to avoid padding. The best you can achieve is a compile time assert giving an error if the struct size doesn't match the sum of all its individual members. When the assert fails, you go and change the compiler settings to block padding. Preferably:
static_assert(sizeof(mystruct) == (sizeof(mystruct.x) + sizeof(mystruct.y) +...));
If static_assert isn't available in your C compiler (you'd need a C11 compliant one), then use some "ct_assert" macro, you can find plenty on this site.
However, this doesn't solve the problem in a portable manner, you would be dependent on compiler settings. The only truly portable way to solve the problem is something like this:
void mystruct_copy_from (uint8_t* raw_data, const mystruct_t* ms)
{
memcpy(raw_data, &ms->x, sizeof(ms->x));
raw_data += sizeof(ms->x);
memcpy(raw_data, &ms->y, sizeof(ms->y));
raw_data += sizeof(ms->y);
// ... and so on
}
uint8_t protocol [EXPECTED_STRUCT_SIZE];
mystruct_copy_from (protocol, &mystruct);
send(protocol); // some data communication interface without padding
Upvotes: 1
Reputation: 755064
Pack the biggest items at the start of the structure. Pack smaller items towards the end.
struct optimal_packing
{
double d;
int i[4];
short j[3];
char s[24];
};
To be slightly more accurate, it is the items with the most stringent alignment requirements that need to come earliest (which tend to be pointers and double
or perhaps long double
), and those with less stringent alignment requirements at the end (short
and char
). You can still end up with tail padding if the total length of the components adds up to, say 35 bytes, but one of the types requires an 8-byte alignment; there'd be 5 bytes of padding.
Upvotes: 7