第3章: 基本構文と変数
学習目標
この章を終えると、以下ができるようになります:
- Zigの基本的な構文規則を理解する
- 変数と定数の宣言方法を使い分けられる
- Zigのプリミティブ型を理解し、適切に使用できる
- 型推論と明示的な型指定を使い分けられる
基本構文
ソースファイルの構造
Zigのソースファイルは、以下の要素で構成されます:
// 1. インポート文
const std = @import("std");
// 2. 定数定義
const VERSION = "1.0.0";
const MAX_SIZE = 1024;
// 3. 型定義
const Point = struct {
x: i32,
y: i32,
};
// 4. 関数定義
pub fn main() void {
std.debug.print("Hello, Zig!\n", .{});
}
// 5. テスト
test "basic test" {
try std.testing.expectEqual(@as(i32, 4), 2 + 2);
}
コメント
// これは単一行コメント
/// これはドキュメントコメント
/// 関数や型の説明に使用
pub fn add(a: i32, b: i32) i32 {
return a + b;
}
//! これはモジュールレベルのドキュメントコメント
//! ファイルの先頭に書く
Zigには複数行コメント(/ /)はありません。
セミコロンとブロック
// 文の終わりにはセミコロンが必要
const x = 10;
const y = 20;
// ブロックは波括弧で囲む
{
const z = 30;
// zはこのブロック内でのみ有効
}
// 式としてのブロック
const result = blk: {
const a = 10;
const b = 20;
break :blk a + b; // 30
};
変数と定数
const vs var
Zigには2種類の変数宣言があります:
const std = @import("std");
pub fn example() void {
// const: 不変(変更不可)
const x = 10;
// x = 20; // コンパイルエラー!
// var: 可変(変更可能)
var y = 10;
y = 20; // OK
std.debug.print("x={}, y={}\n", .{x, y});
}
原則: デフォルトで const を使用し、必要な場合のみ var を使用します。
変数の初期化
// 明示的な型指定
const x: i32 = 10;
// 型推論
const y = 10; // i32と推論される
// 未初期化変数(undefined)
var z: i32 = undefined;
z = 100; // 後で初期化
// 複数の変数宣言
const a: i32 = 1;
const b: i32 = 2;
const c: i32 = 3;
スコープとシャドーイング
const std = @import("std");
pub fn example() void {
const x = 10;
{
const x = 20; // シャドーイング(外側のxを隠す)
std.debug.print("Inner x: {}\n", .{x}); // 20
}
std.debug.print("Outer x: {}\n", .{x}); // 10
}
プリミティブ型
整数型
Zigは豊富な整数型を提供します:
// 符号付き整数
const i8_val: i8 = -128; // 8ビット
const i16_val: i16 = -32768; // 16ビット
const i32_val: i32 = -2147483648; // 32ビット
const i64_val: i64 = -9223372036854775808; // 64ビット
const i128_val: i128 = -170141183460469231731687303715884105728; // 128ビット
// 符号なし整数
const u8_val: u8 = 255; // 8ビット
const u16_val: u16 = 65535; // 16ビット
const u32_val: u32 = 4294967295; // 32ビット
const u64_val: u64 = 18446744073709551615; // 64ビット
// 任意ビット幅の整数
const u5_val: u5 = 31; // 5ビット(0-31)
const i3_val: i3 = -4; // 3ビット(-4 to 3)
// プラットフォーム依存の型
const isize_val: isize = -100; // ポインタサイズの符号付き整数
const usize_val: usize = 100; // ポインタサイズの符号なし整数
浮動小数点型
const f16_val: f16 = 3.14; // 16ビット浮動小数点
const f32_val: f32 = 3.14159; // 32ビット浮動小数点
const f64_val: f64 = 3.141592653589793; // 64ビット浮動小数点
const f128_val: f128 = 3.141592653589793238462643383279502884; // 128ビット浮動小数点
// 浮動小数点リテラル
const pi = 3.14159; // デフォルトでf64と推論
const e = 2.71828;
// 指数表記
const large = 1.23e10; // 1.23 * 10^10
const small = 1.23e-10; // 1.23 * 10^-10
ブール型
const is_true: bool = true;
const is_false: bool = false;
// 論理演算
const and_result = true and false; // false
const or_result = true or false; // true
const not_result = !true; // false
文字型
// 文字(u8のエイリアス)
const char: u8 = 'A';
const newline: u8 = '\n';
// UTF-8コードポイント(u21)
const unicode_char: u21 = 'あ';
const emoji: u21 = '😀';
const std = @import("std");
pub fn example() void {
std.debug.print("char: {c}\n", .{char});
std.debug.print("unicode: {u}\n", .{unicode_char});
}
void型
// 値を返さない関数
pub fn doSomething() void {
// 何か処理
}
// voidリテラル(使用頻度は低い)
const unit: void = {};
型変換
明示的な型変換
Zigでは暗黙の型変換はありません。すべて明示的に行います:
const std = @import("std");
pub fn example() void {
// 整数型の変換
const x: i32 = 10;
const y: i64 = @intCast(x); // i32 -> i64
// 浮動小数点型の変換
const f: f32 = 3.14;
const d: f64 = @floatCast(f); // f32 -> f64
// 整数から浮動小数点への変換
const i: i32 = 42;
const float_val: f64 = @floatFromInt(i);
// 浮動小数点から整数への変換
const float_num: f64 = 3.7;
const int_val: i32 = @intFromFloat(float_num); // 3(切り捨て)
std.debug.print("y={}, d={d:.2}, float_val={d:.1}, int_val={}\n",
.{y, d, float_val, int_val});
}
ビットキャスト
const std = @import("std");
pub fn example() void {
// ビットパターンを維持したまま型を変換
const x: u32 = 0x3f800000;
const f: f32 = @bitCast(x); // 1.0
std.debug.print("Bit pattern 0x{x} as f32: {d:.1}\n", .{x, f});
}
数値リテラル
整数リテラル
// 10進数
const decimal = 1234;
// 16進数(0xプレフィックス)
const hex = 0xFF; // 255
// 8進数(0oプレフィックス)
const octal = 0o755; // 493
// 2進数(0bプレフィックス)
const binary = 0b1111; // 15
// アンダースコアで区切り(可読性向上)
const large_number = 1_000_000;
const hex_color = 0xFF_00_FF;
const binary_mask = 0b1111_0000;
浮動小数点リテラル
// 通常の表記
const normal = 3.14159;
// 指数表記
const scientific = 6.022e23;
// 16進数浮動小数点
const hex_float = 0x1.0p10; // 1.0 * 2^10 = 1024.0
型推論とコンパイル時型
型推論
const std = @import("std");
pub fn example() void {
// 型推論
const x = 10; // i32と推論
const y = 3.14; // f64と推論
const z = true; // boolと推論
// 型を取得
const T1 = @TypeOf(x); // i32
const T2 = @TypeOf(y); // f64
std.debug.print("Type of x: {}\n", .{T1});
std.debug.print("Type of y: {}\n", .{T2});
}
comptime型
// コンパイル時にのみ存在する型
const comptime_int = 42; // comptime_intとして推論
const comptime_float = 3.14; // comptime_floatとして推論
// 実行時の型に変換
const runtime_int: i32 = comptime_int;
const runtime_float: f64 = comptime_float;
型エイリアス
// 型に別名をつける
const MyInt = i32;
const MyFloat = f64;
pub fn example() void {
const x: MyInt = 10;
const y: MyFloat = 3.14;
}
演算子
算術演算子
const std = @import("std");
pub fn example() void {
const a: i32 = 10;
const b: i32 = 3;
// 基本演算
const add = a + b; // 13
const sub = a - b; // 7
const mul = a * b; // 30
const div = @divTrunc(a, b); // 3(切り捨て除算)
const mod = @mod(a, b); // 1(剰余)
// ラップアラウンド演算(オーバーフロー時に折り返す)
var x: u8 = 255;
x +%= 1; // 0
// 飽和演算(オーバーフロー時に最大値/最小値)
var y: u8 = 255;
y +|= 1; // 255
std.debug.print("add={}, sub={}, mul={}, div={}, mod={}\n",
.{add, sub, mul, div, mod});
}
比較演算子
const std = @import("std");
pub fn example() void {
const a = 10;
const b = 20;
const eq = (a == b); // false(等しい)
const ne = (a != b); // true(等しくない)
const lt = (a < b); // true(小さい)
const le = (a <= b); // true(以下)
const gt = (a > b); // false(大きい)
const ge = (a >= b); // false(以上)
std.debug.print("eq={}, ne={}, lt={}, le={}, gt={}, ge={}\n",
.{eq, ne, lt, le, gt, ge});
}
ビット演算子
const std = @import("std");
pub fn example() void {
const a: u8 = 0b1100;
const b: u8 = 0b1010;
const and_result = a & b; // 0b1000 (ビットAND)
const or_result = a | b; // 0b1110 (ビットOR)
const xor_result = a ^ b; // 0b0110 (ビットXOR)
const not_result = ~a; // 0b11110011 (ビットNOT)
const left_shift = a << 2; // 0b110000 (左シフト)
const right_shift = a >> 2; // 0b0011 (右シフト)
std.debug.print("and={b:08}, or={b:08}, xor={b:08}\n",
.{and_result, or_result, xor_result});
}
論理演算子
const a = true;
const b = false;
const and_result = a and b; // false
const or_result = a or b; // true
const not_result = !a; // false
デバッグ出力
std.debug.print
const std = @import("std");
pub fn example() void {
// 基本的な出力
std.debug.print("Hello, World!\n", .{});
// 変数の出力
const x = 42;
std.debug.print("x = {}\n", .{x});
// 複数の変数
const y = 3.14;
const z = true;
std.debug.print("x={}, y={d:.2}, z={}\n", .{x, y, z});
// フォーマット指定子
std.debug.print("{s}\n", .{"文字列"}); // 文字列
std.debug.print("{}\n", .{42}); // 整数
std.debug.print("{d:.2}\n", .{3.14159}); // 小数(2桁)
std.debug.print("{b}\n", .{@as(u8, 15)}); // 2進数
std.debug.print("{x}\n", .{@as(u8, 255)}); // 16進数
std.debug.print("{o}\n", .{@as(u8, 8)}); // 8進数
std.debug.print("{c}\n", .{@as(u8, 'A')}); // 文字
}
練習例
例1: 型の範囲を調べる
const std = @import("std");
pub fn main() void {
// u8の範囲
const u8_min: u8 = 0;
const u8_max: u8 = 255;
std.debug.print("u8: {} to {}\n", .{u8_min, u8_max});
// i8の範囲
const i8_min: i8 = -128;
const i8_max: i8 = 127;
std.debug.print("i8: {} to {}\n", .{i8_min, i8_max});
// u16の範囲
const u16_max: u16 = 65535;
std.debug.print("u16: 0 to {}\n", .{u16_max});
}
例2: 型変換の実践
const std = @import("std");
pub fn main() void {
// 整数から浮動小数点
const int_val: i32 = 42;
const float_val: f64 = @floatFromInt(int_val);
std.debug.print("{} as f64: {d:.1}\n", .{int_val, float_val});
// 浮動小数点から整数
const pi: f64 = 3.14159;
const pi_int: i32 = @intFromFloat(pi);
std.debug.print("{d:.5} as i32: {}\n", .{pi, pi_int});
// 異なる整数型の変換
const small: i16 = 100;
const large: i32 = @intCast(small);
std.debug.print("i16 {} as i32: {}\n", .{small, large});
}
例3: ビット演算の活用
const std = @import("std");
pub fn main() void {
// フラグの管理
const FLAG_READ: u8 = 0b0001;
const FLAG_WRITE: u8 = 0b0010;
const FLAG_EXECUTE: u8 = 0b0100;
var permissions: u8 = FLAG_READ | FLAG_WRITE; // 読み書き
// 実行権限を追加
permissions |= FLAG_EXECUTE;
// 権限のチェック
const can_read = (permissions & FLAG_READ) != 0;
const can_write = (permissions & FLAG_WRITE) != 0;
const can_execute = (permissions & FLAG_EXECUTE) != 0;
std.debug.print("Permissions: {b:08}\n", .{permissions});
std.debug.print("Read: {}, Write: {}, Execute: {}\n",
.{can_read, can_write, can_execute});
}
まとめ
この章では、Zigの基本構文と変数について学びました:
const(不変)と var(可変)次の章では、Zigの型システムについてさらに深く学びます。