Brainlag
  1. Library
  2. Examples
  3. C

A hex dump of a struct

Print every byte of a game save struct in hex and see the fields, the little-endian ints and the padding bytes nobody asked for.

C15 min

What to look at

dump gets any block of memory as const unsigned char *, so it can read it one byte at a time. Before filling the struct, memset paints every byte aa, so any byte that still says aa afterwards is padding: space the compiler left so each int starts at an address that is a multiple of 4. Ints are stored little-endian: the lowest byte first, so 1000 (hex 3e8) shows up as e8 03 00 00. Moving the fields around changes the size.

Run it: bytes of a save file

Run it, read it, change it. Open in playground keeps your own copy.

main.c to run
#include <stdio.h>
#include <string.h>
#include <stddef.h>

struct save {
    char slot;        // 1 byte
    int coins;        // 4 bytes
    char difficulty;  // 1 byte
    int level;        // 4 bytes
};

struct save_packed {
    int coins;
    int level;
    char slot;
    char difficulty;
};

void dump(const char *label, const void *mem, int size) {
    const unsigned char *b = mem;
    printf("%s (%d bytes)\n", label, size);
    for (int i = 0; i < size; i++) {
        printf("%02x ", b[i]);
        if (i % 8 == 7 || i == size - 1) {
            printf("\n");
        }
    }
}

int main(void) {
    struct save s;
    memset(&s, 0xaa, sizeof s);
    s.slot = 'A';
    s.coins = 1000;
    s.difficulty = 2;
    s.level = 7;

    dump("struct save", &s, sizeof s);
    printf("offsets: slot %d, coins %d, difficulty %d, level %d\n",
           (int)offsetof(struct save, slot), (int)offsetof(struct save, coins),
           (int)offsetof(struct save, difficulty), (int)offsetof(struct save, level));

    int wasted = 0;
    const unsigned char *b = (const unsigned char *)&s;
    for (int i = 0; i < (int)sizeof s; i++) {
        if (b[i] == 0xaa) {
            wasted++;
        }
    }
    printf("padding bytes: %d\n\n", wasted);

    struct save_packed p;
    memset(&p, 0xaa, sizeof p);
    p.coins = 1000;
    p.level = 7;
    p.slot = 'A';
    p.difficulty = 2;
    dump("struct save_packed", &p, sizeof p);
    return 0;
}

Change it

  • Set coins to -1 and read the four bytes. Then try 256 and 65536: which byte changes?
  • Add a double best_time; field after slot in struct save. How many padding bytes appear now, and where?
  • Change char slot; to char name[5]; and give it "Kai" with strcpy. Can you spot the '\0' and the bytes after it?
  • Remove the memset lines. What do the padding bytes show now, and why can you not rely on them?

Code editor. Press Control or Command plus Enter to run the code. Tab indents; to move focus out of the editor, press Escape and then Tab.