mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
Merge pull request #21741 from kj4tmp/langref-packed-structs
langref: improve packed struct memory layout description
This commit is contained in:
commit
3746b3d93c
@ -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#}
|
||||
|
||||
19
doc/langref/packed_struct_mmio.zig
Normal file
19
doc/langref/packed_struct_mmio.zig
Normal file
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user