みなさん、こんにちは。システムエンジニアのおみつです。
今回の記事はZigの変数とデータ型に焦点を当て、その基本から応用までを解説します。
変数の基本
変数は、データを保存するための名前付きの箱のようなものです。
Zigでは、変数を宣言する際にはvar
キーワードを使用します。
var myVariable: i32 = 10;
このコードでは、i32
型呼ばれるデータ型のmyVariable
という名前の変数を宣言し、10
という値を代入しています。
次の章では様々なデータ型について詳しく解説します。
整数型
Zigには、異なるサイズと符号の整数型が用意されています。
変数に代入されるであろう数値がある程度決まっている場合、その数値にあった整数型を使うようにしましょう。
符号付き整数型
i8
: -128から127までの値を表現できます。i16
: -32,768から32,767までの値を表現できます。i32
: -2,147,483,648から2,147,483,647までの値を表現できます。i64
: -9,223,372,036,854,775,808から9,223,372,036,854,775,807を表現できます。
符号なし整数型
u8
: 0から255までの値を表現できます。u16
: 0から65,535までの値を表現できます。u32
: 0から4,294,967,295の値を表現できます。u64
: 0から18,446,744,073,709,551,615の値を表現できます。
何故、整数型のサイズをしっかり意識したほうがいいのかについては以下の記事で詳しく解説しています。是非、読んでみて下さい。

浮動小数点型
次に実数を表現するためのデータ型です。Zigには以下の浮動小数点型があります。
f16
: 16ビットの浮動小数点数f32
: 32ビットの浮動小数点数f64
: 64ビットの浮動小数点数
var pi: f32 = 3.141592653589793238;
上記のように、変数を宣言し実数を代入します。
文字と文字列
Zigでは、文字はu8
型で、文字列は[]const u8
型で表現されます。
var character: u8 = 'A';
var string: []const u8 = "Hello, Zig!";
例えば以下のようなコードがあったとします。
const std = @import("std"); pub fn main() void { var character: u8 = 'A'; var string: []const u8 = "Hello, Zig!"; std.debug.print("Character: {}\n", .{character}); std.debug.print("String: {}\n", .{string}); }
実行結果は以下の通りです。
Character: 65
String: Hello, Zig!
以下、同じ文字なのに結果が異なることについて気になるそこのアナタ向けです。
興味のある方は読んでみて下さい。
‘A’は65なのに、Hello, Zig!は文字列として表示されるのはなぜ?
Zigには、他の多くのプログラミング言語のような専用の「文字」型は存在しません。代わりに、Zigでは文字をu8
(8ビットの符号なし整数)として表現します。このため、'A'
はu8
型の値65
として解釈されます。
文字列に関しても、Zigには専用の「文字列」型は存在しません。代わりに、文字列はバイトの配列、具体的には[]const u8
として表現されます。このため、"Hello, Zig!"
は[]const u8
型のバイトの配列として解釈されます。
上記の例では、Zigのstd.debug.print
関数で結果をコンソールに表示させています。
単一のu8
値を数値として解釈し、その数値を出力します。したがって、'A'
はASCII値65
として表示されます。
一方、std.debug.print
関数は、[]const u8
型の配列("Hello, Zig!"
)を文字列として解釈します。このため、バイトの配列はそのままの文字列として出力されます。
フォーマット文字列の{}
プレースホルダは、与えられた引数の型に基づいて動的にフォーマットを適用します。したがって、u8
型の値は数値として、[]const u8
型の配列は文字列として解釈されます。
この動作は、std.debug.print
関数の設計に基づいており、Zigの型システムと組み合わせて、異なる型のデータを適切に表示するためのものです。
ブール型
真または偽の値を持つ型です。Zigのブール型はbool
で、true
またはfalse
の値を持ちます。
var isBig: bool = true;
var isSmall: bool = false;
上記の真偽値以外を代入すると、コンパイル時にエラーになります。
厳密にいうとコンパイル時エラーとなるのは、その変数の利用するときです。
宣言するだけでは現時点のZIgコンパイラではエラーをなりません。(2023/08/29執筆時点)
配列
配列は、同じ型のデータを複数持つことができるデータ構造です。
var numbers: [3]i32 = [i32]{1, 2, 3};
このコードでは、i32
型のデータを3つ持つ配列numbers
を宣言しています。
ポインタ
ポインタは、メモリ上の特定のアドレスを指すデータ型です。ポインタを使用すると、変数のメモリアドレスに直接アクセスしたり、そのアドレスにデータを保存したりすることができます。
少し複雑なのでZigにおけるポインタの宣言と使用について詳しく解説します。
ポインタの宣言
var number: i32 = 10;
var pointerToNumber: *i32 = &number;
ここで、&number
は変数number
のメモリアドレス(10)を取得します。そして、そのアドレスはpointerToNumber
という名前のポインタ変数に保存されます。
ポインタを通じた値のアクセス
const valueAtAddress = *pointerToNumber;
*pointerToNumber
はポインタをデリファレンスする操作で、これによりポインタが指すアドレスの値にアクセスできます。
つまりこの場合、valueAtAddress
は10
になります。
ポインタを通じた値の変更
*pointerToNumber = 20;
ポインタをデリファレンスして値を変更することで、元の変数の値も変更されます。
つまりこの場合、変数number
の値が20
に変更されます。
nullポインタ
var nullPointer: ?*i32 = null;
Zigでは、?
を使用してnull可能なポインタを宣言できます。主に次の目的で利用されることが多いです。
- 初期化
- 終端マーカー
- エラーチェック
- リソースの解放
ポインタの配列
var numbers: [3]i32 = [3]i32{10, 20, 30};
var pointers: [*]i32 = &numbers;
この例では、numbers配列の各要素へのポインタをpointers配列に保存しています。
これらの基本的な操作を理解することで、Zigにおけるポインタの動作と使用方法についての基本的な知識を得ることができます。ポインタは非常に強力なツールであり、メモリの効率的な管理やデータ構造の実装など、多くの高度なタスクに使用されます。
まとめ
この記事では、Zigの変数とデータ型についての基本を解説しました。これらの知識をもとに、Zigでのプログラミングの基礎を固めることができるでしょう。次回は、Zigの演算子について詳しく解説しますので、お楽しみに!
コメント