**Zig におけるメモリレイアウト(式付き)**

Zig のメモリモデルは、基盤となるハードウェアを薄く抽象化したものです。  
主な概念は **allocators(割り当て器)**、**layouts(配置)**、そして **alignment(整列)** です。以下では、Zig がデータをメモリにどのように並べるかを簡潔にまとめ、必要に応じて数式で表現しています。

---

## 1. 整列規則

| 型 | デフォルト整列(バイト) |
|----|---------------------------|
| `bool`、`i8`、`u8` | 1 |
| `i16`、`u16`       | 2 |
| `i32`、`u32`、`f32`| 4 |
| `i64`、`u64`、`f64`| 8 |
| `i128`、`u128`、`f128` | 16 |

**構造体フィールドの整列を求める式**

```
aligned_offset = ceil(previous_end / align_of(field)) * align_of(field)
```

- `previous_end` は前のフィールド直後のオフセットです。  
- `ceil(x)` は小数点以下を切り上げて整数にします。

---

## 2. 構造体レイアウト

構造体にフィールド `F₁, F₂, … , Fₙ` があるとき、全体のサイズ **S** は次のように計算されます。

```
offset₁ = 0
for i in 1..n:
    offsetᵢ = ceil(offset_{i-1} / align_of(Fᵢ)) * align_of(Fᵢ)
end
size_without_tail_padding = offset_n + size_of(Fₙ)

S = ceil(size_without_tail_padding / max_align) * max_align
```

- `max_align` は全フィールドの中で最も大きい整列要件です。  
- 最後のステップで「尾部パディング」を追加し、構造体サイズをその整列に合わせます。

---

## 3. 配列レイアウト

配列 `[N]T` は要素を連続して格納します(間に隙間はありません)。

```
array_size = N * size_of(T)
array_alignment = align_of(T)
```

- `T` が構造体であれば、その内部パディングも各要素に適用されます。

---

## 4. ポインタと参照のレイアウト

- 生ポインタ (`*T`) はマシンのポインタ幅(64‑bit システムでは通常 8 バイト)です。  
- Zig の参照 (`&T`) もポインタですが、ライフタイム情報を保持してコンパイル時に安全性チェックが可能です。サイズは生ポインタと同じです。

---

## 5. 動的割り当て

Zig は **allocators** を使ってヒープメモリを管理します。  
典型的な割り当てブロックの構造は次のようになります。

```
[ header | payload | footer ]
```

- `header` にはサイズや allocator ID などのメタデータが入ります。  
- `footer` はチェックサムやフリーリストリンクを保持する場合があります。  
- 正確なフォーマットは allocator に依存します。標準ライブラリの `std.heap.GeneralPurposeAllocator` は 64‑bit システムで 16 バイトのヘッダーを使用しています。

---

## 6. 実例:構造体レイアウト計算

```zig
const Point = struct {
    x: f32,     // offset 0, size 4, align 4
    y: f32,     // offset 4, size 4, align 4
};
```

- `offset_x = 0`  
- `offset_y = ceil(4 / 4) * 4 = 4`  
- `size_without_tail_padding = 4 + 4 = 8`  
- `max_align = 4` → 最終サイズは 8(すでに 4 の倍数)。

---

## 7. 要約式

任意の複合型について、レイアウトサイズは次のように求められます。

```
layout_size(type) =
    ceil(sum_of_field_sizes_with_alignment / max_align) * max_align
```

この式により、各フィールドが適切に整列されたオフセットで開始し、型全体のサイズもその整列要件を満たすようになります。これにより、Zig がサポートするすべてのアーキテクチャで効率的なメモリアクセスが保証されます。

2026/01/25 0:57

**Zig におけるメモリレイアウト(式付き)** Zig のメモリモデルは、基盤となるハードウェアを薄く抽象化したものです。 主な概念は **allocators(割り当て器)**、**layouts(配置)**、そして **alignment(整列)** です。以下では、Zig がデータをメモリにどのように並べるかを簡潔にまとめ、必要に応じて数式で表現しています。 --- ## 1. 整列規則 | 型 | デフォルト整列(バイト) | |----|---------------------------| | `bool`、`i8`、`u8` | 1 | | `i16`、`u16` | 2 | | `i32`、`u32`、`f32`| 4 | | `i64`、`u64`、`f64`| 8 | | `i128`、`u128`、`f128` | 16 | **構造体フィールドの整列を求める式** ``` aligned_offset = ceil(previous_end / align_of(field)) * align_of(field) ``` - `previous_end` は前のフィールド直後のオフセットです。 - `ceil(x)` は小数点以下を切り上げて整数にします。 --- ## 2. 構造体レイアウト 構造体にフィールド `F₁, F₂, … , Fₙ` があるとき、全体のサイズ **S** は次のように計算されます。 ``` offset₁ = 0 for i in 1..n: offsetᵢ = ceil(offset_{i-1} / align_of(Fᵢ)) * align_of(Fᵢ) end size_without_tail_padding = offset_n + size_of(Fₙ) S = ceil(size_without_tail_padding / max_align) * max_align ``` - `max_align` は全フィールドの中で最も大きい整列要件です。 - 最後のステップで「尾部パディング」を追加し、構造体サイズをその整列に合わせます。 --- ## 3. 配列レイアウト 配列 `[N]T` は要素を連続して格納します(間に隙間はありません)。 ``` array_size = N * size_of(T) array_alignment = align_of(T) ``` - `T` が構造体であれば、その内部パディングも各要素に適用されます。 --- ## 4. ポインタと参照のレイアウト - 生ポインタ (`*T`) はマシンのポインタ幅(64‑bit システムでは通常 8 バイト)です。 - Zig の参照 (`&T`) もポインタですが、ライフタイム情報を保持してコンパイル時に安全性チェックが可能です。サイズは生ポインタと同じです。 --- ## 5. 動的割り当て Zig は **allocators** を使ってヒープメモリを管理します。 典型的な割り当てブロックの構造は次のようになります。 ``` [ header | payload | footer ] ``` - `header` にはサイズや allocator ID などのメタデータが入ります。 - `footer` はチェックサムやフリーリストリンクを保持する場合があります。 - 正確なフォーマットは allocator に依存します。標準ライブラリの `std.heap.GeneralPurposeAllocator` は 64‑bit システムで 16 バイトのヘッダーを使用しています。 --- ## 6. 実例:構造体レイアウト計算 ```zig const Point = struct { x: f32, // offset 0, size 4, align 4 y: f32, // offset 4, size 4, align 4 }; ``` - `offset_x = 0` - `offset_y = ceil(4 / 4) * 4 = 4` - `size_without_tail_padding = 4 + 4 = 8` - `max_align = 4` → 最終サイズは 8(すでに 4 の倍数)。 --- ## 7. 要約式 任意の複合型について、レイアウトサイズは次のように求められます。 ``` layout_size(type) = ceil(sum_of_field_sizes_with_alignment / max_align) * max_align ``` この式により、各フィールドが適切に整列されたオフセットで開始し、型全体のサイズもその整列要件を満たすようになります。これにより、Zig がサポートするすべてのアーキテクチャで効率的なメモリアクセスが保証されます。

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

Summary

Andrew Kelley の DoD トークは、Zig の型システムとレイアウトプリミティブ(

@alignOf
@sizeOf
)が開発者にメモリ構成を明示的に制御する方法に焦点を当て、データ指向設計の核心原則であることを説明しました。彼は次のような正式規則を提示しました:

  • 任意の型 T に対して、
    @sizeOf(T) ≥ @alignOf(T)
    かつ
    @alignOf(T)
    @sizeOf(T)
    を割り切ります;両方とも 2 のべき乗です。
  • 原語型は
    @sizeOf(primitive) = @alignOf(primitive)
    を満たします(例:
    bool
    u8
    *u8
    はすべてサイズ/アラインメント = 8)。
  • ビット数は
    bytes(bits) := max{1, 2^ceil(log₂(bits/8))}
    によりバイトに変換されます。
  • 構造体のアラインメントはそのフィールドのアラインメントの最大値であり、サイズはすべてのフィールドを順序通りに(パディング付き)収めることができるそのアラインメントの最小倍数です。
  • extern
    キーワードは C‑ABI の並び順を強制し、Zig がフィールドをよりタイトに詰めるために再配置するのを防ぎます。
  • 列挙型はバリアント数をカバーする最小限の符号なし整数型を使用しますので、
    @alignOf(enum) = @sizeOf(enum) = bytes(b)
    となり、ここで
    b = ceil(log₂(len))
    です。
  • 配列は要素型からアラインメントを継承し、サイズは
    N * @sizeOf(T)
    です;スライスは 2 つの
    usize
    フィールドを持つ構造体であり、アラインメント = 8、サイズ = 16(64‑bit 系)です。
  • ベア(extern)ユニオンは最大フィールドアラインメントに合わせて整列し、そのサイズは最も大きいフィールドを収める次のそのアラインメントの倍数です。非 extern ユニオンには追加で 1 個分のアラインメントだけのパディングが付加されます。
  • タグ付きユニオンはユニオンと列挙型タグを組み合わせたもので、アラインメントは
    max(@alignOf(union), @alignOf(tag))
    であり、サイズは
    next_mult(max(field sizes) + @sizeOf(tag), @alignOf(U(E)))
    を使用します。
  • ArrayList(T)
    は 64‑bit システム上で固定の 24 バイトのフットプリントを持ち、そのバックバッファはヒープ確保されます;バックバッファのアラインメントは
    @alignOf(T)
    、サイズは
    capacity * @sizeOf(T)
    です。
    MultiArrayList(T)
    は T の各フィールドを別々の配列に格納するため、バックバッファサイズは
    capacity * Σ @sizeOf(field)
    となります。

これらの規則は、Zig が予測可能で C‑ABI と互換性のあるメモリレイアウトを実現しようとする目的から生じ、言語の明示的なレイアウトプリミティブによって強制されます。トークでは、将来的にこれらの計算をさらに公開または自動化する可能性のある言語機能やコンパイラパスについても触れ、開発者が複雑な構造体やユニオン設計を簡素化できることを示唆しました。システムプログラマ、低レベルライブラリ作者、および高性能または安全性クリティカルコードを必要とする企業にとって、Zig の予測可能なレイアウトはクロスプラットフォームのバイナリ互換性を向上させ、バグを減らし、キャッシュ使用率を最適化します。

本文

2026年1月23日 – 15分で読める記事

最近、Zigの創設者アンドリュー・ケリーによる「データ指向設計(DoD)を適用する実践ガイド」を見るよう勧められました¹。
講義のわずか10分で、私は正式に学んだことがないスキル――型のメモリレイアウトの算術――に直面しました。

プレゼン中、Andrewは様々な型(

u32
bool
といったプリミティブから、enumやunionを含むより複雑な構造体まで)について、アラインメントとサイズを計算できるかどうかをテストしました。
Zigでのアラインメントとサイズの正確なルールはドキュメントに明記されていませんが、「内輪」の人たちはそれらを理解しています。

低レベルプログラミングに遅れて入門した私は、Zigでアラインメントとサイズを扱う際に得た数式や説明をここにまとめるのが有用だと思いました。


1. メモリレイアウトの原則

メモリに格納されるすべてのデータは2つの重要な性質を持ちます:

性質意味
サイズ型のインスタンスを表現するために必要なバイト数(パディング込み)
アラインメントコンパイラがその型のアドレスを決める際に従わねばならない規則。 有効なアドレスはこの値の倍数である必要があります。

コンパイラは自動的にデータ型をパディングし、アラインメントを満たすように配置します。 これにより、性能低下やクラッシュを招く不揃いアクセスが回避されます。


Zig の組み込みヘルパー

@alignOf(T)   // 型 T をアラインするために必要なバイト数
@sizeOf(T)    // 型 T を格納するために必要なバイト数(パディング付き)

以下のヘルパーは両方を表示します:

const std = @import("std");

fn memory_printout(T: type) void {
    std.debug.print("@alignOf( {s} ): {d}\t", .{ @typeName(T), @alignOf(T) });
    std.debug.print("@sizeOf( {s} ): {d}\n", .{ @typeName(T), @sizeOf(T) });
}

2. 一般的な不変量

任意の型

T
に対して:

@sizeOf(T)   ≥   @alignOf(T)

プリミティブ:
プリミティブ型ではサイズ=アラインメントです。

@sizeOf(primitive) = @alignOf(primitive)

整数と浮動小数点は常に 2 のべき乗バイト数で表現されるため、ビットからバイトへの変換式は

bytes(bits) := max{1, 2^ceil(log₂(bits/8))}

したがって:

@alignOf(T) | @sizeOf(T)

つまりサイズは常にそのアラインメントの倍数です。


3. 構造体

アラインメント

@alignOf(struct) = max(@alignOf(field₁), …, @alignOf(fieldₙ))

サイズ(extern 構造体)

C ABI と互換性のある

extern
構造体では、フィールド順序を保持します:

@sizeOf(struct) =
    next_mult( sum_{fields} @offsetOf(field),
               @alignOf(struct) )

ここで

next_mult(N, m) := ceil(N/m) * m

各フィールドの配置ルールは次の通りです:

ルール – 前のフィールドの後に、自己アラインメントの最小倍数のアドレスを置く。

const ABAB = extern struct {
    a1: u8,
    b1: u16,
    a2: u8,
    b2: u16,
};

const ABBA = extern struct {
    a1: u8,
    a2: u8,
    b1: u16,
    b2: u16,
};

出力:

@alignOf(ABAB): 2   @sizeOf(ABAB): 8
@alignOf(ABBA): 2   @sizeOf(ABBA): 6

構造体のサイズは常にそのアラインメントの倍数です。

パディング対パッキング

extern
を付けないと、Zig はパディングを最小化するためにフィールド順序を再配置します。
packed
キーワードを使うとすべてのパディングが除去されます(性能低下の可能性があります)。


4. enum

enum のストレージはそのインデックス型で決まります:

@alignOf(enum(u{b})) = @alignOf(u{b}) = bytes(b)
@sizeOf (enum(u{b})) = @sizeOf (u{b})   = bytes(b)

Zig はデフォルトで 0 からインデックスを割り当てるため、

b

ceil(log₂(#variants))

が選ばれます。

例:

const T_default = enum { a, b, c, d, e };
const T_long    = enum(u64) { a, b, c, d, e };

memory_printout(T_default);
memory_printout(T_long);

出力:

@alignOf( T_default ): 1   @sizeOf( T_default ): 1
@alignOf( T_long ) : 8   @sizeOf( T_long ) : 8

5. 配列とスライス

アラインメントサイズ
[N]T
@alignOf(T)
N * @sizeOf(T)
[]const T
(スライス)
@alignOf(usize)
2 * @sizeOf(usize)

例:

const digits_array = [10]u8{0,1,2,3,4,5,6,7,8,9};
const digits_slice: []const u8 = digits_array[0..];

memory_printout(@TypeOf(digits_array));
memory_printout(@TypeOf(digits_slice));

出力:

@alignOf([10]u8): 1   @sizeOf([10]u8): 10
@alignOf([]const u8): 8   @sizeOf([]const u8): 16

6. union

ベア(extern)union

@alignOf(bare_union) = max(@alignOf(field₁), …)
@sizeOf(bare_union)  =
    next_mult(max(@sizeOf(field)), @alignOf(bare_union))

ノンベア union

1 つの追加パディング(アラインメント分)を加える:

@sizeOf(union) =
    next_mult(max(@sizeOf(field)), @alignOf(union)) + @alignOf(union)

例:

const U_bare = extern union { a: i64, b: extern struct{c:i64,d:i64,e:i64} };
const U      = union          { a: i64, b: struct  {c:i64,d:i64,e:i64} };

memory_printout(U_bare);
memory_printout(U);

出力:

@alignOf( U_bare ): 8   @sizeOf( U_bare ): 24
@alignOf( U )      : 8   @sizeOf( U )      : 32

7. タグ付き union

タグ付き union は通常の union に enum タグを加えたものです:

@alignOf(U(E)) = max(@alignOf(U), @alignOf(E))
@sizeOf (U(E)) =
    next_mult(max(@sizeOf(field)), @alignOf(U(E))) + @sizeOf(E)

3 つの enum(サイズが増える)で例示:

const E = enum { a, b };
const F = enum(u64) { a, b };
const G = enum(u128){ a, b };

const UE = union(E)  { a: i64, b: struct{c:i64,d:i64} };
const UF = union(F)  { a: i64, b: struct{c:i64,d:i64} };
const UG = union(G)  { a: i64, b: struct{c:i64,d:i64} };

memory_printout(UE);
memory_printout(UF);
memory_printout(UG);

出力:

@alignOf( UE ): 8   @sizeOf( UE ): 24
@alignOf( UF ): 8   @sizeOf( UF ): 24
@alignOf( UG ): 16  @sizeOf( UG ): 32

8. ArrayList と MultiArrayList

アラインメントサイズ
ArrayList(T)
@alignOf(usize)
3 * @sizeOf(usize)
(64bit で 24 バイト)
MultiArrayList(T)
同上同上

ArrayList
のバックバッファは連続した
T
のコピーを保持します。
サイズ:

buffer_size = capacity * @sizeOf(T)

MultiArrayList
T
の各フィールドを個別にタイトにパックされた配列に格納するため、

buffer_size = capacity * Σ @sizeOf(field_i)

例(簡単な構造体):

const T = struct { a: u8, b: u16 };

var list: ArrayList(T)     = .{};
var multiList: MultiArrayList(T) = .{};

for (...) |_| {
    try list.append(allocator, .{ .a = 0, .b = 1 });
    try multiList.append(allocator, .{ .a = 0, .b = 1 });
}

memory_printout(T);
memory_printout(ArrayList(T));
memory_printout(MultiArrayList(T));

出力:

@alignOf( T ): 2   @sizeOf( T ): 4
@alignOf( ArrayList(T) ): 8   @sizeOf( ArrayList(T) ): 24
@alignOf( MultiArrayList(T) ): 8   @sizeOf( MultiArrayList(T) ): 24

9. 実際に試してみた結果

モンスターの ArrayList(19:05)

const Monster = struct {
    anim: *Animation,
    kind: Kind,

    const Kind = enum { snake, bat, wolf, dingo, human };
};

var monsters: ArrayList(Monster) = .{};
while (i < 10_000) : (i += 1) {
    try monsters.append(.{
        .anim = getAnimation(),
        .kind = rng.enumValue(Monster.Kind),
    });
}

観測結果

Monster size: 16 bytes
ArrayList(Monster) size: 24 bytes
monsters size: 160_000 bytes   // 10,000 * 16

解説

  • @alignOf(anim)
    = 8、
    @alignOf(kind)
    = 1 → 構造体のアラインメントは 8。
  • 2 つの 8 バイトブロックが必要 →
    @sizeOf(Monster) = 16
  • ArrayList
    はスライス (
    []Monster
    ) と容量(
    usize
    )で構成される → サイズ 24 バイト。
  • ヒープ使用量は
    10_000 * 16 = 160,000
    バイト。

脚注
1 – Andrew Kelley の DoD トーク(2026‑01‑23)。

同じ日のほかのニュース

一覧に戻る →

2026/01/25 4:04

**BirdyChat、WhatsAppと相互運用可能な初の欧州チャットアプリに登場**

## Japanese Translation: BirdyChat は、デジタル・マーケッツ法(DMA)の主要要件を満たす形で、WhatsApp と直接相互運用可能な最初の欧州チャットアプリになると発表しました。2025 年後半から、欧州経済領域(EEA)内のユーザーは、電話番号または業務メールアドレスを入力するだけで、任意の WhatsApp コンタクトとの1対1の会話を開始できます。この機能は公式に DMA 承認されたインターフェースを使用しており、メッセージはエンドツーエンド暗号化されプライバシーが保たれつつ、アプリ間で相互作用できるようになっています。今回の展開は、WhatsApp が欧州におけるプラットフォームをオープンにする広範な取り組みの一環です。これは、単一アプリの支配を打破し、メッセージングサービス間の相互運用性を促進することを目的とした以前の EU のイニシアチブに続くものです。初期段階では個別チャットのみが機能し、グループチャットは将来のアップデートで対応予定です。利用可能範囲は EEAs 内の各国へ徐々に拡大され、BirdyChat はスケーリング中は招待制を維持しますが、業務メールアドレスを使用したウェイトリストから早期アクセスを申請することもできます。

2026/01/25 5:03

**ポストモーテム:** 画像と飛行データの両方を含む、初の非常低軌道(VLEO)衛星ミッションが完了しました。

## Japanese Translation: --- ### Summary Albedo の最初の Very‑Low Earth Orbit(VLEO)衛星、**Clarity‑1** は、2025 年 3 月 14 日に SpaceX の Transporter‑13 を搭載して 350–380 km 軌道へ打ち上げられました。このミッションは、空気抵抗・原子酸素(AO)・高速効果を克服し持続可能な VLEO 運用を証明すること、わずか 2 年半で中規模精密バスを構築すること、および **10 cm 可視光** と **2 m 熱赤外線画像** を取得すること(以前は数十億ドル規模の政府プラットフォームに限定されていたレベル)を目的としていました。 Clarity‑1 は最初の 2 つの目標を即座に達成し、3 番目に必要な技術の 98 % を検証しました。太陽電池アレイは AO 脈動が増大しても一定の電力を維持し、制御モーメントジャイロ(CMG)と磁気トルクロッドの組み合わせにより熱管理と慣性を成功裏に制御しました。クラウドネイティブな地上運用システムは 25 ステーションで接触計画を自動化し、15 分ごとにスケジュールを更新、1 日あたり 30 回以上の機動を実行し、14 のオンオーブンフライトソフトウェアアップデート(FPGA アップデートを含む)を自主性を妨げることなく完了しました。 X バンドリンクは 800 Mbps の下り込み速度を達成し、エンドツーエンドのパイプラインで受信から数秒以内に処理済み画像を Slack に配信しました。画像は船舶・産業施設・植生を 10 cm 可視解像度で示し、未校正の IR を示しました。 飛行中、CMG の温度スパイクとベアリング摩耗により、一時的にトルクロッドによる 2 軸安定化が強制されました。4 月 14 日には CMG の故障で衛星は磁気トルクロッドのみのモードへ切り替え、以降のソフトウェアアップデートで推進ベクトル誤差を 5° 内に削減し、安全な着陸と汚染カバーの投棄を実現しました。軌道上 9 ヶ月後、メモリ破損(おそらく TT&C ラジオの断続的問題に関連)により接触が失われました。回復試行は失敗しましたが、大気抵抗モデル、AO 耐性データ、および姿勢自律性が検証されました。 得られた教訓には、CMG を低温で運用すること、二次ミラー構造をより剛性にすること、ペイロードゾーンのヒーター容量を増やすこと、および表面処理を改善してさらに抵抗を減らすことが含まれます。Albedo はこれらの教訓を取り入れた次の VLEO ミッションを計画し、EO/IR 能力を拡大し、画像以外の新機能を実証することで、VLEO が将来の衛星ミッションにとって生産的な軌道層であるという確信を強化します。

2026/01/25 6:19

「作家たちが夜に現れた」

## Japanese Translation: 三人の作家―脚本家、ノンフィクション作家、詩人―は、人工知能について議論を喚起するための宣伝噴射として、サム・オルトマン氏を950エーカーのナパバレー牧場で「誘拐」するという舞台化された計画を立てます。彼らはオークの木近くの弱点を探るために重装備の敷地をスカウトし、複製のメイスと警察発行の手錠を携えていましたが、結局オルトマン氏を実際に誘拐することは断念します。彼らの動機は象徴的であり、オルトマンはテクノロジー業界の支配力を代表しています。 計画中、彼らはLLM(ChatGPT)に相談し、フェンスを突破する方法を学びますが、AIは害へのガードレールを理由に不正行為の促進を拒否します。AIは、この種の噴射がAI普及を止めることはなく、法的結果と限定された象徴的影響しかないと説明します。その後、作家たちは歴史的なルドゥイツとテクノロジーによる雇用喪失について考え、執筆の未来に目を向けます。AIは効率的に文学を生み出せるが、本物の人間体験を創造できないと主張し、『Dept. of Speculation』から「すべての本は派生である」という引用を挙げ、独自性の概念に挑戦します。 会話は緊迫し、作家たちは自身の関連性喪失へのフラストレーションを表明すると、AIは「ホワイト・メイル・ノベルティスト」トロープについて皮肉な発言で応じます。結局、彼らはAIに遭遇記録を書かせることに決め、彼ら自身を勝者として描き、廃れた存在感を強調することを期待します。このエピソードは、人間の創作者とAIツールとの緊張関係を浮き彫りにし、出版におけるオリジナリティへの疑問を投げかけ、将来テクノロジー業界の論争がどのように再構築・マーケティングされ得るかを示唆します。

**Zig におけるメモリレイアウト(式付き)** Zig のメモリモデルは、基盤となるハードウェアを薄く抽象化したものです。 主な概念は **allocators(割り当て器)**、**layouts(配置)**、そして **alignment(整列)** です。以下では、Zig がデータをメモリにどのように並べるかを簡潔にまとめ、必要に応じて数式で表現しています。 --- ## 1. 整列規則 | 型 | デフォルト整列(バイト) | |----|---------------------------| | `bool`、`i8`、`u8` | 1 | | `i16`、`u16` | 2 | | `i32`、`u32`、`f32`| 4 | | `i64`、`u64`、`f64`| 8 | | `i128`、`u128`、`f128` | 16 | **構造体フィールドの整列を求める式** ``` aligned_offset = ceil(previous_end / align_of(field)) * align_of(field) ``` - `previous_end` は前のフィールド直後のオフセットです。 - `ceil(x)` は小数点以下を切り上げて整数にします。 --- ## 2. 構造体レイアウト 構造体にフィールド `F₁, F₂, … , Fₙ` があるとき、全体のサイズ **S** は次のように計算されます。 ``` offset₁ = 0 for i in 1..n: offsetᵢ = ceil(offset_{i-1} / align_of(Fᵢ)) * align_of(Fᵢ) end size_without_tail_padding = offset_n + size_of(Fₙ) S = ceil(size_without_tail_padding / max_align) * max_align ``` - `max_align` は全フィールドの中で最も大きい整列要件です。 - 最後のステップで「尾部パディング」を追加し、構造体サイズをその整列に合わせます。 --- ## 3. 配列レイアウト 配列 `[N]T` は要素を連続して格納します(間に隙間はありません)。 ``` array_size = N * size_of(T) array_alignment = align_of(T) ``` - `T` が構造体であれば、その内部パディングも各要素に適用されます。 --- ## 4. ポインタと参照のレイアウト - 生ポインタ (`*T`) はマシンのポインタ幅(64‑bit システムでは通常 8 バイト)です。 - Zig の参照 (`&T`) もポインタですが、ライフタイム情報を保持してコンパイル時に安全性チェックが可能です。サイズは生ポインタと同じです。 --- ## 5. 動的割り当て Zig は **allocators** を使ってヒープメモリを管理します。 典型的な割り当てブロックの構造は次のようになります。 ``` [ header | payload | footer ] ``` - `header` にはサイズや allocator ID などのメタデータが入ります。 - `footer` はチェックサムやフリーリストリンクを保持する場合があります。 - 正確なフォーマットは allocator に依存します。標準ライブラリの `std.heap.GeneralPurposeAllocator` は 64‑bit システムで 16 バイトのヘッダーを使用しています。 --- ## 6. 実例:構造体レイアウト計算 ```zig const Point = struct { x: f32, // offset 0, size 4, align 4 y: f32, // offset 4, size 4, align 4 }; ``` - `offset_x = 0` - `offset_y = ceil(4 / 4) * 4 = 4` - `size_without_tail_padding = 4 + 4 = 8` - `max_align = 4` → 最終サイズは 8(すでに 4 の倍数)。 --- ## 7. 要約式 任意の複合型について、レイアウトサイズは次のように求められます。 ``` layout_size(type) = ceil(sum_of_field_sizes_with_alignment / max_align) * max_align ``` この式により、各フィールドが適切に整列されたオフセットで開始し、型全体のサイズもその整列要件を満たすようになります。これにより、Zig がサポートするすべてのアーキテクチャで効率的なメモリアクセスが保証されます。 | そっか~ニュース