alloc() / free()(スライスの確保と解放)
『Zig』では、ヒープメモリの確保・解放は アロケータインターフェース(『std.mem.Allocator』) を通じて行います。『alloc()』と『free()』はスライス(配列)の確保と解放に使い、『create()』と『destroy()』は単一要素のポインタに対応します。また、構造体に『deinit()』メソッドを定義することで、C++ のデストラクタに相当するリソース解放パターンを実現できます。
構文
// -----------------------------------------------
// alloc / free — スライスの確保と解放
// -----------------------------------------------
// 型 T の要素を n 個確保します(失敗時は error.OutOfMemory)
const slice = try allocator.alloc(T, n);
defer allocator.free(slice); // スコープを抜けると自動で解放されます
// -----------------------------------------------
// create / destroy — 単一ポインタの確保と解放
// -----------------------------------------------
// 型 T 1 個分のメモリを確保して *T を返します
const ptr = try allocator.create(T);
defer allocator.destroy(ptr); // スコープを抜けると自動で解放されます
// -----------------------------------------------
// deinit() — デストラクタパターン
// 構造体にリソース解放メソッドを持たせる慣用句です
// -----------------------------------------------
const MyResource = struct {
allocator: std.mem.Allocator,
data: []u8,
pub fn init(allocator: std.mem.Allocator, size: usize) !MyResource {
const data = try allocator.alloc(u8, size);
return .{ .allocator = allocator, .data = data };
}
// deinit() で内部リソースをすべて解放します
pub fn deinit(self: *MyResource) void {
self.allocator.free(self.data);
}
};
var res = try MyResource.init(allocator, 128);
defer res.deinit(); // スコープ終了時に deinit が呼ばれます
主なメソッド一覧
| メソッド | 概要 |
|---|---|
allocator.alloc(T, n) | 型『T』の要素を『n』個ヒープ上に確保し、スライス『[]T』を返します。失敗時は『error.OutOfMemory』を返します。 |
allocator.free(slice) | 『alloc()』で確保したスライスを解放します。『defer』と組み合わせるのが定番パターンです。 |
allocator.create(T) | 型『T』1 個分のメモリを確保してポインタ『*T』を返します。構造体の単一インスタンスをヒープに置く際に使います。 |
allocator.destroy(ptr) | 『create()』で確保したポインタのメモリを解放します。スライスには使えません。 |
allocator.realloc(slice, n) | 既存スライスを新しいサイズ『n』に再確保します。成功すると新しいスライスを返します。 |
deinit() | 構造体が内部で確保したリソースを解放するための慣用メソッド名です。言語仕様で強制されるものではなく、Zig コミュニティの慣例です。 |
defer allocator.free(...) | 確保の直後に『defer』で解放を予約する書き方です。解放忘れを防ぐ Zig の定番パターンです。 |
サンプルコード
yakuza_alloc_free.zig
// yakuza_alloc_free.zig — alloc / free / deinit のサンプルです
// 龍が如くシリーズのキャラクターを使って
// ヒープメモリの確保・解放とデストラクタパターンを確認します
const std = @import("std");
// -----------------------------------------------
// CharacterRecord — キャラクターの戦闘記録を管理する構造体です
// 内部でヒープ確保したデータを持ち、deinit() で解放します
// -----------------------------------------------
const CharacterRecord = struct {
allocator: std.mem.Allocator,
name: []u8, // キャラクター名(ヒープに確保)
combat_log: [][]u8, // 戦闘ログ(各エントリもヒープに確保)
log_count: usize,
// init() — 構造体を初期化してヒープ領域を確保します
pub fn init(allocator: std.mem.Allocator, name: []const u8, max_logs: usize) !CharacterRecord {
// キャラクター名をヒープにコピーします
const name_buf = try allocator.alloc(u8, name.len);
@memcpy(name_buf, name);
// 戦闘ログのスライス配列を確保します
const log_buf = try allocator.alloc([]u8, max_logs);
return .{
.allocator = allocator,
.name = name_buf,
.combat_log = log_buf,
.log_count = 0,
};
}
// addLog() — 戦闘ログを1件追加します
pub fn addLog(self: *CharacterRecord, message: []const u8) !void {
if (self.log_count >= self.combat_log.len) return error.LogFull;
// ログの文字列もヒープに確保します
const entry = try self.allocator.alloc(u8, message.len);
@memcpy(entry, message);
self.combat_log[self.log_count] = entry;
self.log_count += 1;
}
// deinit() — 構造体が保持するすべてのヒープ領域を解放します
// C++ のデストラクタに相当するパターンです
pub fn deinit(self: *CharacterRecord) void {
// 各ログエントリを個別に解放します
var i: usize = 0;
while (i < self.log_count) : (i += 1) {
self.allocator.free(self.combat_log[i]);
}
// スライス配列本体を解放します
self.allocator.free(self.combat_log);
// キャラクター名のバッファを解放します
self.allocator.free(self.name);
}
// printLogs() — 保持するログを標準出力に表示します
pub fn printLogs(self: *const CharacterRecord, writer: anytype) !void {
try writer.print("【{s}】の戦闘記録\n", .{self.name});
var i: usize = 0;
while (i < self.log_count) : (i += 1) {
try writer.print(" [{d}] {s}\n", .{ i + 1, self.combat_log[i] });
}
}
};
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// GeneralPurposeAllocator を使います(デバッグビルドでリーク検出が有効になります)
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
try stdout.print("=== 龍が如く 戦闘記録システム ===\n\n", .{});
// -----------------------------------------------
// CharacterRecord を init() で確保し defer deinit() で解放します
// -----------------------------------------------
// 桐生一馬の戦闘記録を作成します
var kiryu = try CharacterRecord.init(allocator, "桐生一馬", 8);
defer kiryu.deinit(); // スコープ終了時に全ヒープ領域が解放されます
try kiryu.addLog("極道の王道・龍が如く — 神室町ヒートアクション発動");
try kiryu.addLog("スタイル切替:ラッシュスタイルで速攻連打");
try kiryu.addLog("必殺技:猛虎落地式を発動して敵3体を一掃");
try kiryu.printLogs(stdout);
try stdout.print("\n", .{});
// 真島吾朗の戦闘記録を作成します
var majima = try CharacterRecord.init(allocator, "真島吾朗", 8);
defer majima.deinit();
try majima.addLog("マッドドッグスタイル — 短刀乱舞で敵陣を制圧");
try majima.addLog("ビートスタイル — バットを片手に踊りながら乱入");
try majima.addLog("眠れる獅子が目覚めた — クレイジードッグ発動");
try majima.printLogs(stdout);
try stdout.print("\n", .{});
// -----------------------------------------------
// alloc / free — 龍が如く7キャラクターの名前リストを確保します
// -----------------------------------------------
try stdout.print("--- alloc / free でキャラクター名リストを確保 ---\n", .{});
const character_names = [_][]const u8{
"春日一番",
"南雲清人",
"足立宏一",
"アダチ警部補",
"内田晴海",
};
// キャラクター名ポインタの配列をヒープに確保します
const name_list = try allocator.alloc([]u8, character_names.len);
defer {
// defer ブロック内で各エントリを解放してから配列本体を解放します
for (name_list) |entry| {
allocator.free(entry);
}
allocator.free(name_list);
}
// 各名前をヒープにコピーします
for (character_names, 0..) |src, i| {
const buf = try allocator.alloc(u8, src.len);
@memcpy(buf, src);
name_list[i] = buf;
}
try stdout.print("ドラゴンカートメンバー:\n", .{});
for (name_list) |name| {
try stdout.print(" - {s}\n", .{name});
}
try stdout.print("\n", .{});
// -----------------------------------------------
// create / destroy — 単一構造体をヒープに確保します
// -----------------------------------------------
try stdout.print("--- create / destroy で単一構造体を確保 ---\n", .{});
const BattleResult = struct {
winner: []const u8, // 勝者の名前
hp_remaining: u32, // 残り HP
battle_time_sec: f64, // 戦闘時間(秒)
};
// create() は型 1 個分のメモリを確保して *T を返します
const result = try allocator.create(BattleResult);
defer allocator.destroy(result); // スコープ終了時にポインタのメモリを解放します
result.* = BattleResult{
.winner = "桐生一馬",
.hp_remaining = 847,
.battle_time_sec = 42.5,
};
try stdout.print("戦闘結果\n", .{});
try stdout.print(" 勝者 : {s}\n", .{result.winner});
try stdout.print(" 残り HP : {d}\n", .{result.hp_remaining});
try stdout.print(" 戦闘時間 : {d:.1}秒\n", .{result.battle_time_sec});
}
zig run yakuza_alloc_free.zig === 龍が如く 戦闘記録システム === 【桐生一馬】の戦闘記録 [1] 極道の王道・龍が如く — 神室町ヒートアクション発動 [2] スタイル切替:ラッシュスタイルで速攻連打 [3] 必殺技:猛虎落地式を発動して敵3体を一掃 【真島吾朗】の戦闘記録 [1] マッドドッグスタイル — 短刀乱舞で敵陣を制圧 [2] ビートスタイル — バットを片手に踊りながら乱入 [3] 眠れる獅子が目覚めた — クレイジードッグ発動 --- alloc / free でキャラクター名リストを確保 --- ドラゴンカートメンバー: - 春日一番 - 南雲清人 - 足立宏一 - アダチ警部補 - 内田晴海 --- create / destroy で単一構造体を確保 --- 戦闘結果 勝者 : 桐生一馬 残り HP : 847 戦闘時間 : 42.5秒
概要
『Zig』の『alloc()』、『free()』は「スライスを返すペア」、『create()』、『destroy()』は「単一ポインタを返すペア」という役割分担があります。どちらも『defer』を使って確保の直後に解放を予約するのが定番の書き方で、関数の途中でエラーが発生してもリソースが確実に解放されます。『deinit()』メソッドパターンは言語仕様として強制されるものではなく Zig コミュニティの慣例ですが、標準ライブラリ全体で一貫して採用されているため、自作の構造体でも同じ命名にすることでコードの読み手の理解コストを下げられます。利用するアロケータについては アロケータ基本、詳細な GPA の使い方は GeneralPurposeAllocator も合わせて参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。