C Structure Padding Initialization | Interrupt

This article takes a look at a few different aspects of C structure initialization. In particular, we’ll look at when it matters, the current state of things in Clang and GCC, recommendations, and the ✨ future ✨.

This is a companion discussion topic for the original entry at https://interrupt.memfault.com/blog/c-struct-padding-initialization

A great article. For an optimized and/or secure system, it’s important to pay attention to these small details of how things look in memory.

However, I can’t believe I didn’t know about the pahole tool - I’ve been manually inserting those offset comments when optimizing…

I recently wrote an article about a similar topic - and one thing I wrote about is what unaligned accesses look like in memory and why they are bad or simply not allowed:


That’s some great data, thanks Noah! Sound conclusions too. I think in C it pays to be explicit.

A potentially useful technique to enable this practice: union with a byte array. In some scenarios (eg. serialisation) I’ve found this useful to have a convenient record based syntax as well as contiguous byte access so you don’t have to rely on implicit assumptions.

For example:

struct foo {
  uint32_t i;
  uint8_t b;

union fooWithByteAccess {
  foo fields;
  uint8_t bytes[sizeof(foo)];

You can then avoid memcpy and use an initialiser instead:

union fooWithByteAccess f = {.bytes = {0}};

You still need to be very careful - reading and writing to a union using different members is called “type punning”, and if the members are different sizes, or you’re using C90, there are really nasty dragons. See here for some confusing clarifications - as always, trust but verify!

A working example:

#include <stdio.h>
#include <stdint.h>

struct foo {
  uint32_t i;
  uint8_t b;

union fooWithByteAccess {
  struct foo fields;
  uint8_t bytes[sizeof(struct foo)];

int main(int argc, char *argv[])
  union fooWithByteAccess f = {.bytes = {0}};
  f.fields.i = 1234;
  f.fields.b = 123;

  printf("sizeof(f) = %lu\n", sizeof(f));
  printf("f.bytes[0] = 0x%02X\n", f.bytes[0]);
  printf("f.bytes[1] = 0x%02X\n", f.bytes[1]);
  printf("f.bytes[2] = 0x%02X\n", f.bytes[2]);
  printf("f.bytes[3] = 0x%02X\n", f.bytes[3]);
  printf("f.bytes[4] = 0x%02X\n", f.bytes[4]);
  printf("f.bytes[5] = 0x%02X\n", f.bytes[5]);
  printf("f.bytes[6] = 0x%02X\n", f.bytes[6]);
  printf("f.bytes[7] = 0x%02X\n", f.bytes[7]);

  return 0;
1 Like

This has always seemed like a common usage pattern. However, I see a lot of articles on this topic lately.
A great article. :+1: