parent
0c5f897904
commit
d0c39895aa
|
@ -8,13 +8,7 @@
|
|||
body{
|
||||
background-color:#111;
|
||||
color: #bbb;
|
||||
font-family: system-ui,
|
||||
/* Fallbacks for browsers that don't support system-ui */
|
||||
/* https://caniuse.com/#search=system-ui */
|
||||
-apple-system, /* iOS and macOS */
|
||||
Roboto, /* Android */
|
||||
"Segoe UI", /* Windows */
|
||||
sans-serif;
|
||||
font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif;
|
||||
}
|
||||
a {
|
||||
color: #88f;
|
||||
|
@ -707,15 +701,21 @@ fn divide(a: i32, b: i32) i32 {
|
|||
{#code_end#}
|
||||
<p>
|
||||
In this function, values {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are known only at runtime,
|
||||
and thus this division operation is vulnerable to both integer overflow and
|
||||
division by zero.
|
||||
and thus this division operation is vulnerable to both {#link|Integer Overflow#} and
|
||||
{#link|Division by Zero#}.
|
||||
</p>
|
||||
<p>
|
||||
Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on
|
||||
integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and
|
||||
{#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.
|
||||
</p>
|
||||
{#see_also|Integer Overflow|Division by Zero|Wrapping Operations#}
|
||||
<p>
|
||||
Zig supports arbitrary bit-width integers, referenced by using
|
||||
an identifier of <code>i</code> or </code>u</code> followed by digits. For example, the identifier
|
||||
{#syntax#}i7{#endsyntax#} refers to a signed 7-bit integer. The maximum allowed bit-width of an
|
||||
integer type is {#syntax#}65535{#endsyntax#}.
|
||||
</p>
|
||||
{#see_also|Wrapping Operations#}
|
||||
{#header_close#}
|
||||
{#header_close#}
|
||||
{#header_open|Floats#}
|
||||
|
@ -2047,13 +2047,203 @@ test "linked list" {
|
|||
}
|
||||
{#code_end#}
|
||||
{#header_open|packed struct#}
|
||||
<p>{#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout.</p>
|
||||
<p>TODO bit fields</p>
|
||||
<p>TODO alignment</p>
|
||||
<p>TODO endianness</p>
|
||||
<p>TODO @bitOffsetOf and @byteOffsetOf</p>
|
||||
<p>TODO mention how volatile loads and stores of bit packed fields could be more efficient when
|
||||
done by hand instead of with packed struct</p>
|
||||
<p>
|
||||
Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Fields remain in the order declared.</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>A {#link|packed 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>Non-byte-aligned fields are packed into the smallest possible
|
||||
byte-aligned integers in accordance with the target endianness.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
This means that a {#syntax#}packed struct{#endsyntax#} can participate
|
||||
in a {#link|@bitCast#} or a {#link|@ptrCast#} to reinterpret memory.
|
||||
This even works at {#link|comptime#}:
|
||||
</p>
|
||||
{#code_begin|test#}
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const Full = packed struct {
|
||||
number: u16,
|
||||
};
|
||||
const Divided = packed struct {
|
||||
half1: u8,
|
||||
quarter3: u4,
|
||||
quarter4: u4,
|
||||
};
|
||||
|
||||
test "@bitCast between packed structs" {
|
||||
doTheTest();
|
||||
comptime doTheTest();
|
||||
}
|
||||
|
||||
fn doTheTest() void {
|
||||
assert(@sizeOf(Full) == 2);
|
||||
assert(@sizeOf(Divided) == 2);
|
||||
var full = Full{ .number = 0x1234 };
|
||||
var divided = @bitCast(Divided, full);
|
||||
switch (builtin.endian) {
|
||||
builtin.Endian.Big => {
|
||||
assert(divided.half1 == 0x12);
|
||||
assert(divided.quarter3 == 0x3);
|
||||
assert(divided.quarter4 == 0x4);
|
||||
},
|
||||
builtin.Endian.Little => {
|
||||
assert(divided.half1 == 0x34);
|
||||
assert(divided.quarter3 == 0x2);
|
||||
assert(divided.quarter4 == 0x1);
|
||||
},
|
||||
}
|
||||
}
|
||||
{#code_end#}
|
||||
<p>
|
||||
Zig allows the address to be taken of a non-byte-aligned field:
|
||||
</p>
|
||||
{#code_begin|test#}
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const BitField = packed struct {
|
||||
a: u3,
|
||||
b: u3,
|
||||
c: u2,
|
||||
};
|
||||
|
||||
var foo = BitField{
|
||||
.a = 1,
|
||||
.b = 2,
|
||||
.c = 3,
|
||||
};
|
||||
|
||||
test "pointer to non-byte-aligned field" {
|
||||
const ptr = &foo.b;
|
||||
assert(ptr.* == 2);
|
||||
}
|
||||
{#code_end#}
|
||||
<p>
|
||||
However, the pointer to a non-byte-aligned field has special properties and cannot
|
||||
be passed when a normal pointer is expected:
|
||||
</p>
|
||||
{#code_begin|test_err|expected type#}
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const BitField = packed struct {
|
||||
a: u3,
|
||||
b: u3,
|
||||
c: u2,
|
||||
};
|
||||
|
||||
var bit_field = BitField{
|
||||
.a = 1,
|
||||
.b = 2,
|
||||
.c = 3,
|
||||
};
|
||||
|
||||
test "pointer to non-bit-aligned field" {
|
||||
assert(bar(&bit_field.b) == 2);
|
||||
}
|
||||
|
||||
fn bar(x: *const u3) u3 {
|
||||
return x.*;
|
||||
}
|
||||
{#code_end#}
|
||||
<p>
|
||||
In this case, the function {#syntax#}bar{#endsyntax#} cannot be called becuse the pointer
|
||||
to the non-byte-aligned field mentions the bit offset, but the function expects a byte-aligned pointer.
|
||||
</p>
|
||||
<p>
|
||||
Pointers to non-byte-aligned fields share the same address as the other fields within their host integer:
|
||||
</p>
|
||||
{#code_begin|test#}
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const BitField = packed struct {
|
||||
a: u3,
|
||||
b: u3,
|
||||
c: u2,
|
||||
};
|
||||
|
||||
var bit_field = BitField{
|
||||
.a = 1,
|
||||
.b = 2,
|
||||
.c = 3,
|
||||
};
|
||||
|
||||
test "pointer to non-bit-aligned field" {
|
||||
assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.b));
|
||||
assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.c));
|
||||
}
|
||||
{#code_end#}
|
||||
<p>
|
||||
This can be observed with {#link|@bitOffsetOf#} and {#link|byteOffsetOf#}:
|
||||
</p>
|
||||
{#code_begin|test#}
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const BitField = packed struct {
|
||||
a: u3,
|
||||
b: u3,
|
||||
c: u2,
|
||||
};
|
||||
|
||||
test "pointer to non-bit-aligned field" {
|
||||
comptime {
|
||||
assert(@bitOffsetOf(BitField, "a") == 0);
|
||||
assert(@bitOffsetOf(BitField, "b") == 3);
|
||||
assert(@bitOffsetOf(BitField, "c") == 6);
|
||||
|
||||
assert(@byteOffsetOf(BitField, "a") == 0);
|
||||
assert(@byteOffsetOf(BitField, "b") == 0);
|
||||
assert(@byteOffsetOf(BitField, "c") == 0);
|
||||
}
|
||||
}
|
||||
{#code_end#}
|
||||
<p>
|
||||
Packed structs have 1-byte alignment. However if you have an overaligned pointer to a packed struct,
|
||||
Zig should correctly understand the alignment of fields. However there is
|
||||
<a href="https://github.com/ziglang/zig/issues/1994">a bug</a>:
|
||||
</p>
|
||||
{#code_begin|test_err#}
|
||||
const S = packed struct {
|
||||
a: u32,
|
||||
b: u32,
|
||||
};
|
||||
test "overaligned pointer to packed struct" {
|
||||
var foo: S align(4) = undefined;
|
||||
const ptr: *align(4) S = &foo;
|
||||
const ptr_to_b: *u32 = &ptr.b;
|
||||
}
|
||||
{#code_end#}
|
||||
<p>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.
|
||||
</p>
|
||||
<p>
|
||||
It's also
|
||||
<a href="https://github.com/ziglang/zig/issues/1512">planned to be able to set alignment of struct fields</a>.
|
||||
</p>
|
||||
<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.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|struct Naming#}
|
||||
<p>Since all structs are anonymous, Zig infers the type name based on a few rules.</p>
|
||||
|
@ -2203,8 +2393,8 @@ export fn entry(foo: Foo) void { }
|
|||
{#header_close#}
|
||||
{#header_open|packed enum#}
|
||||
<p>By default, the size of enums is not guaranteed.</p>
|
||||
<p>{#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the integer tag type
|
||||
of the enum:</p>
|
||||
<p>{#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the
|
||||
integer tag type of the enum:</p>
|
||||
{#code_begin|test#}
|
||||
const std = @import("std");
|
||||
|
||||
|
@ -2217,6 +2407,7 @@ test "packed enum" {
|
|||
std.debug.assert(@sizeOf(Number) == @sizeOf(u8));
|
||||
}
|
||||
{#code_end#}
|
||||
<p>This makes the enum eligible to be in a {#link|packed struct#}.</p>
|
||||
{#header_close#}
|
||||
{#see_also|@memberName|@memberCount|@tagName|@sizeOf#}
|
||||
{#header_close#}
|
||||
|
@ -2344,7 +2535,12 @@ test "@tagName" {
|
|||
Unions with an enum tag are generated as a struct with a tag field and union field. Zig
|
||||
sorts the order of the tag and union field by the largest alignment.
|
||||
</p>
|
||||
{#header_open|packed union#}
|
||||
<p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible
|
||||
to be in a {#link|packed struct#}.
|
||||
{#header_close#}
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|blocks#}
|
||||
<p>
|
||||
Blocks are used to limit the scope of variable declarations:
|
||||
|
|
Loading…
Reference in New Issue
Block a user