🎓 A1Learner
🔍
0 of 46 lessons completed

Zig Packed Structs

A packed struct is a struct whose fields are stored back to back with no padding between them. It is useful when every bit counts, like a hardware register or a file header.

No padding

A packed struct holds three fields in one byte. The plain struct needs two bytes for similar fields.

const std = @import("std");
const Flags = packed struct {
    a: bool,
    b: u3,
    c: u4,
};
const Normal = struct {
    a: bool,
    b: u7,
};
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    try out.print("packed size={d} plain size={d}\n", .{ @sizeOf(Flags), @sizeOf(Normal) });
}

Bit offsets

Each field starts at a known bit. The offsets show where each field begins.

const std = @import("std");
const Flags = packed struct {
    a: bool,
    b: u3,
    c: u4,
};
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    try out.print("bit a={d} b={d} c={d}\n", .{ @bitOffsetOf(Flags, "a"), @bitOffsetOf(Flags, "b"), @bitOffsetOf(Flags, "c") });
}

One integer, two views

A packed struct can be read as one integer. The first field fills the lowest bits.

const std = @import("std");
const Pair = packed struct {
    low: u8,
    high: u8,
};
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    const p = Pair{ .low = 34, .high = 12 };
    try out.print("pair={d} low={d} high={d}\n", .{ @as(u16, @bitCast(p)), p.low, p.high });
    const q: Pair = @bitCast(@as(u16, 0x1234));
    try out.print("q low={d} high={d}\n", .{ q.low, q.high });
}

Example

const std = @import("std");
const Rgb = packed struct {
    r: u5,
    g: u6,
    b: u5,
};
pub fn main() !void {
    const out = std.io.getStdOut().writer();
    try out.print("size={d}\n", .{@sizeOf(Rgb)});
    const red = Rgb{ .r = 31, .g = 0, .b = 0 };
    try out.print("red bits={d}\n", .{@as(u16, @bitCast(red))});
    const blue = Rgb{ .r = 0, .g = 0, .b = 31 };
    try out.print("blue bits={d}\n", .{@as(u16, @bitCast(blue))});
}

Key points

  • Packed struct: a packed struct is a struct whose fields are packed with no padding between them.
  • Padding: padding is unused space that a plain struct inserts between its fields.
  • Bit offset: a bit offset is a field position counted in bits from the start of the struct.
  • Backing integer: a backing integer is the single integer that holds every bit of a packed struct.
  • Field order: field order is the order fields are declared, and packed fields fill bits in that order from the lowest bit upward.
  • Packed size: the packed size is the total of the field widths, rounded up to whole bytes.

The colour packs three fields into two bytes. Red fills the lowest five bits. Blue fills the highest five bits. The backing integer shows the exact bit layout.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

A packed struct has fields a: bool, b: u3, c: u4. How many bytes does it take?