commit 3746b3d93ce895131ab1b2dea5391e5efa9fccf7 (tree)
parent ae38fc6a50dfcece964d7d19d9e35ac5dc583daa
Author: Andrew Kelley <andrew@ziglang.org>
Date: Wed, 16 Apr 2025 04:56:22 -0400
Merge pull request #21741 from kj4tmp/langref-packed-structs
langref: improve packed struct memory layout description
Diffstat:
2 files changed, 44 insertions(+), 19 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
@@ -1649,6 +1649,7 @@ unwrapped == 1234{#endsyntax#}</pre>
<li>{#link|Floats#}</li>
<li>{#link|bool|Primitive Types#}</li>
<li>{#link|type|Primitive Types#}</li>
+ <li>{#link|packed struct#}</li>
</ul>
</td>
<td>
@@ -2224,20 +2225,24 @@ or
{#header_open|packed struct#}
<p>
- Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout:
+ {#syntax#}packed{#endsyntax#} structs, like {#syntax#}enum{#endsyntax#}, are based on the concept
+ of interpreting integers differently. All packed structs have a <strong>backing integer</strong>,
+ which is implicitly determined by the total bit count of fields, or explicitly specified.
+ Packed structs have well-defined memory layout - exactly the same ABI as their backing integer.
+ </p>
+ <p>
+ Each field of a packed struct is interpreted as a logical sequence of bits, arranged from
+ least to most significant. Allowed field types:
</p>
<ul>
- <li>Fields remain in the order declared, least to most significant.</li>
- <li>There is no padding between fields.</li>
- <li>Zig supports arbitrary width {#link|Integers#} 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.
- </li>
- <li>{#syntax#}bool{#endsyntax#} fields use exactly 1 bit.</li>
+ <li>An {#link|integer|Integers#} field uses exactly as many bits as its
+ bit width. For example, a {#syntax#}u5{#endsyntax#} will use 5 bits of
+ the backing integer.</li>
+ <li>A {#link|bool|Primitive Types#} field uses exactly 1 bit.</li>
<li>An {#link|enum#} field uses exactly the bit width of its integer tag type.</li>
<li>A {#link|packed union#} field uses exactly the bit width of the union field with
the largest bit width.</li>
- <li>Packed structs support equality operators.</li>
+ <li>A {#syntax#}packed struct{#endsyntax#} field uses the bits of its backing integer.</li>
</ul>
<p>
This means that a {#syntax#}packed struct{#endsyntax#} can participate
@@ -2245,10 +2250,11 @@ or
This even works at {#link|comptime#}:
</p>
{#code|test_packed_structs.zig#}
-
<p>
- The backing integer is inferred from the fields' total bit width.
- Optionally, it can be explicitly provided and enforced at compile time:
+ The backing integer can be inferred or explicitly provided. When
+ inferred, it will be unsigned. When explicitly provided, its bit width
+ will be enforced at compile time to exactly match the total bit width of
+ the fields:
</p>
{#code|test_missized_packed_struct.zig#}
@@ -2290,18 +2296,18 @@ or
<p>
Equating packed structs results in a comparison of the backing integer,
- and only works for the `==` and `!=` operators.
+ and only works for the {#syntax#}=={#endsyntax#} and {#syntax#}!={#endsyntax#} {#link|Operators#}.
</p>
{#code|test_packed_struct_equality.zig#}
<p>
- Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.
- For details on this subscribe to
- <a href="https://github.com/ziglang/zig/issues/1761">this issue</a>.
- 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.
+ Field access and assignment can be understood as shorthand for bitshifts
+ on the backing integer. These operations are not {#link|atomic|Atomics#},
+ so beware using field access syntax when combined with memory-mapped
+ input-output (MMIO). Instead of field access on {#link|volatile#} {#link|Pointers#},
+ construct a fully-formed new value first, then write that value to the volatile pointer.
</p>
+ {#code|packed_struct_mmio.zig#}
{#header_close#}
{#header_open|Struct Naming#}
diff --git a/doc/langref/packed_struct_mmio.zig b/doc/langref/packed_struct_mmio.zig
@@ -0,0 +1,19 @@
+pub const GpioRegister = packed struct(u8) {
+ GPIO0: bool,
+ GPIO1: bool,
+ GPIO2: bool,
+ GPIO3: bool,
+ reserved: u4 = 0,
+};
+
+const gpio: *volatile GpioRegister = @ptrFromInt(0x0123);
+
+pub fn writeToGpio(new_states: GpioRegister) void {
+ // Example of what not to do:
+ // BAD! gpio.GPIO0 = true; BAD!
+
+ // Instead, do this:
+ gpio.* = new_states;
+}
+
+// syntax