struct

    1. Test 1/4 dot product...OK
    2. Test 2/4 struct namespaced variable...OK
    3. Test 3/4 field parent pointer...OK
    4. Test 4/4 linked list...OK
    5. All tests passed.

    An extern struct has in-memory layout guaranteed to match the C ABI for the target.

    This kind of struct should only be used for compatibility with the C ABI. Every other use case should be solved with or normal struct.

    See also:

    Unlike normal structs, packed structs have guaranteed in-memory layout:

    • Fields remain in the order declared.
    • There is no padding between fields.
    • Zig supports arbitrary width and although normally, integers with fewer than 8 bits will still use 1 byte of memory, in packed structs, they use exactly their bit width.
    • bool fields use exactly 1 bit.
    • A packed enum field uses exactly the bit width of its integer tag type.
    • A field uses exactly the bit width of the union field with the largest bit width.
    • Non-ABI-aligned fields are packed into the smallest possible ABI-aligned integers in accordance with the target endianness.

    This means that a packed struct can participate in a @bitCast or a to reinterpret memory. This even works at comptime:

    test.zig

    1. const std = @import("std");
    2. const builtin = @import("builtin");
    3. const assert = std.debug.assert;
    4. const Full = packed struct {
    5. number: u16,
    6. };
    7. const Divided = packed struct {
    8. half1: u8,
    9. quarter3: u4,
    10. quarter4: u4,
    11. };
    12. test "@bitCast between packed structs" {
    13. doTheTest();
    14. comptime doTheTest();
    15. }
    16. fn doTheTest() void {
    17. assert(@sizeOf(Full) == 2);
    18. assert(@sizeOf(Divided) == 2);
    19. var full = Full{ .number = 0x1234 };
    20. var divided = @bitCast(Divided, full);
    21. switch (builtin.endian) {
    22. builtin.Endian.Big => {
    23. assert(divided.half1 == 0x12);
    24. assert(divided.quarter3 == 0x3);
    25. assert(divided.quarter4 == 0x4);
    26. },
    27. builtin.Endian.Little => {
    28. assert(divided.half1 == 0x34);
    29. assert(divided.quarter3 == 0x2);
    30. assert(divided.quarter4 == 0x1);
    31. },
    32. }
    1. $ zig test test.zig
    2. Test 1/1 @bitCast between packed structs...OK
    3. All tests passed.

    Zig allows the address to be taken of a non-byte-aligned field:

    1. const assert = std.debug.assert;
    2. const BitField = packed struct {
    3. a: u3,
    4. b: u3,
    5. c: u2,
    6. };
    7. var foo = BitField{
    8. .a = 1,
    9. .b = 2,
    10. .c = 3,
    11. };
    12. test "pointer to non-byte-aligned field" {
    13. const ptr = &foo.b;
    14. assert(ptr.* == 2);
    15. }

    However, the pointer to a non-byte-aligned field has special properties and cannot be passed when a normal pointer is expected:

    test.zig

    1. const std = @import("std");
    2. const assert = std.debug.assert;
    3. const BitField = packed struct {
    4. a: u3,
    5. b: u3,
    6. c: u2,
    7. };
    8. var bit_field = BitField{
    9. .a = 1,
    10. .b = 2,
    11. .c = 3,
    12. };
    13. test "pointer to non-bit-aligned field" {
    14. assert(bar(&bit_field.b) == 2);
    15. }
    16. fn bar(x: *const u3) u3 {
    17. return x.*;
    18. }
    1. $ zig test test.zig
    2. /home/andy/dev/zig/docgen_tmp/test.zig:17:26: error: expected type '*const u3', found '*align(:3:1) u3'
    3. assert(bar(&bit_field.b) == 2);
    4. ^

    In this case, the function bar cannot be called becuse the pointer to the non-ABI-aligned field mentions the bit offset, but the function expects an ABI-aligned pointer.

    Pointers to non-ABI-aligned fields share the same address as the other fields within their host integer:

    test.zig

    1. const std = @import("std");
    2. const assert = std.debug.assert;
    3. a: u3,
    4. b: u3,
    5. c: u2,
    6. };
    7. var bit_field = BitField{
    8. .a = 1,
    9. .b = 2,
    10. .c = 3,
    11. };
    12. test "pointer to non-bit-aligned field" {
    13. assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.b));
    14. assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.c));
    15. }
    1. $ zig test test.zig
    2. Test 1/1 pointer to non-bit-aligned field...OK
    3. All tests passed.

    This can be observed with and byteOffsetOf:

    test.zig

    1. $ zig test test.zig
    2. Test 1/1 pointer to non-bit-aligned field...OK
    3. All tests passed.

    test.zig

    1. const S = packed struct {
    2. a: u32,
    3. b: u32,
    4. };
    5. test "overaligned pointer to packed struct" {
    6. var foo: S align(4) = undefined;
    7. const ptr: *align(4) S = &foo;
    8. const ptr_to_b: *u32 = &ptr.b;
    9. }
    1. $ zig test test.zig
    2. /home/andy/dev/zig/docgen_tmp/test.zig:8:32: error: expected type '*u32', found '*align(1) u32'
    3. const ptr_to_b: *u32 = &ptr.b;
    4. ^

    When this bug is fixed, the above test in the documentation will unexpectedly pass, which will cause the test suite to fail, notifying the bug fixer to update these docs.

    It's also .

    Using packed structs with volatile is problematic, and may be a compile error in the future. For details on this subscribe to . TODO update these docs with a recommendation on how to use packed structs with MMIO (the use case for volatile packed structs) once this issue is resolved. Don't worry, there will be a good solution for this use case in zig.

    Since all structs are anonymous, Zig infers the type name based on a few rules.

    • If the struct is in the initialization expression of a variable, it gets named after that variable.
    • If the struct is in the return expression, it gets named after the function it is returning from, with the parameter values serialized.
    • Otherwise, the struct gets a name such as (anonymous struct at file.zig:7:38).

    struct_name.zig

    1. const std = @import("std");
    2. pub fn main() void {
    3. const Foo = struct {};
    4. std.debug.warn("variable: {}\n", @typeName(Foo));
    5. std.debug.warn("anonymous: {}\n", @typeName(struct {}));
    6. std.debug.warn("function: {}\n", @typeName(List(i32)));
    7. }
    8. fn List(comptime T: type) type {
    9. return struct {
    10. x: T,
    11. }

    See also: