デフォルトでは、構造体、列挙型、共用体のメモリ配置は保証されていません。すなわち、これらの値がメモリ上で何バイトを占めるのか、構造体のフィールドがどういう順番で配置されるのか、は決まったルールがありません。
default.zen
を次の内容で作成します。@byteOffsetOf
は、構造体の先頭からフィールドへのバイトオフセットを取得する組込み関数です。
const MyStruct = struct {
a: u8,
b: u64,
};
pub fn main() void {
const a_pos = @byteOffsetOf(MyStruct, "a");
const b_pos = @byteOffsetOf(MyStruct, "b");
std.debug.warn("position of a: {}\n", .{@to(usize, a_pos)});
std.debug.warn("position of b: {}\n", .{@to(usize, b_pos)});
}
MyStruct
は1バイトのフィールドa
と8バイトのフィールドb
を持っています。素直に考えるとa
のオフセットが0
で、b
のオフセットが1
になりそうなものですが、そうはなりません。このコードを実行すると、各フィールドが配置されている場所が出力されます。
$ zen run default.zen
position of a: 0
position of b: 8
b
はMyStruct
の先頭からオフセット8
バイトの位置に配置されています。この結果からわかることは、a
とb
との間に7
バイトのパディングが挿入されているということです。場合によっては、フィールドの順番を入れ替えるかもしれません。
Zenではデータのメモリ配置を制御することができます。C ABIと互換性のある形式でメモリ配置したい場合はextern
を、一切の順番入れ替えとパディングなしでメモリ配置したい場合はpacked
を使用します。
extern struct
/ extern enum
/ extern union
のメモリ配置は、C ABIと互換性があることが保証されます。これらのメモリ配置は、C ABIとの互換性が必要な場合のみ利用します。
packed struct
/ packed enum
/ packed union
はメモリ配置を明確に定義しています。
packed struct
は、struct
と以下の点で異なります。
u1
など) はそのビット幅の分だけメモリを使用します@bitCast
が利用できます先ほどstruct
ではパディングが挿入されたMyStruct
をpacked struct
にしてみましょう。今度は、b
のバイトオフセットは1
になっており、パディングが挿入されていないことがわかります。また、フィールドの順序は宣言順であることも保証されます。
examples/ch11-advanced/memory_layout/src/packed.zen:4:12
const MyStruct = packed struct {
a: u8,
b: u64,
};
test "packed struct" {
ok(@byteOffsetOf(MyStruct, "a") == 0);
ok(@byteOffsetOf(MyStruct, "b") == 1);
}
packed struct
はビット幅分だけメモリを利用するため、ビットフィールドに利用できます。次のBitField
構造体は、1バイトを3つのビットフィールドに分割しています。
examples/ch11-advanced/memory_layout/src/packed.zen:14:18
const BitField = packed struct {
a: u1,
b: u5,
c: u2,
};
BitField
構造体の値は1バイトであることが保証されます。全てのフィールドのバイトオフセットは0
になります。より細かなオフセットを知りたい場合は、ビットオフセットを取得する@bitOffsetOf
を使用します。各ビットフィールドへのポインタを取得することも可能ですが、特別なアライメントを持つポインタ型になることに注意して下さい。
examples/ch11-advanced/memory_layout/src/packed.zen:20:50
test "bitfield" {
// `BitField`のサイズは1バイトであることが保証される
ok(@sizeOf(BitField) == 1);
// バイトオフセットは全て`0`になる
ok(@byteOffsetOf(BitField, "a") == 0);
ok(@byteOffsetOf(BitField, "b") == 0);
ok(@byteOffsetOf(BitField, "c") == 0);
// ビットオフセットにより宣言順にパディングなしで
// 配置されていることがわかる
ok(@bitOffsetOf(BitField, "a") == 0);
ok(@bitOffsetOf(BitField, "b") == 1);
ok(@bitOffsetOf(BitField, "c") == 6);
const x = BitField {
.a = 1,
.b = 2,
.c = 3,
};
// ビットフィールドへのアクセス
ok(x.a == 1);
// ビットフィールドのポインタを取得することも可能
const ptr = &x.b;
ok(ptr.* == 2);
// Compile error:
// error[E02046]: expected '*u5', found '*align(:1:1) u5'
// const ng: *u5 = &x.b;
}
ブール型やpacked enum
型のビット幅も保証されるため、ベアメタル / 組込みでの制御レジスタの定義や、通信プロトコルのヘッダ定義などにも便利です。
32ビット (4バイト) の制御レジスタをブール型やpacked enum
を使って定義する例は以下のとおりです。
examples/ch11-advanced/memory_layout/src/packed.zen:52:68
pub const GpioPortId = packed enum(u1) {
Port0 = 0,
Port1 = 1,
};
const PinSelect = packed struct {
pin: u5 = 0,
port: GpioPortId = .Port0,
reserved: u25 = 0,
connected: bool = false,
};
test "control register example" {
ok(@sizeOf(PinSelect) == 4);
// ビット幅が同じ型から`@bitCast`可能
const pin: PinSelect = @bitCast(PinSelect, @to(u32, 0));
}
☰ 人の生きた証は永遠に残るよう ☰
Copyright © 2018-2020 connectFree Corporation. All rights reserved. | 特定商取引法に基づく表示
Zen, the Zen three-circles logo and The Zen Programming Language are trademarks of connectFree corporation in Japan and other countries.