課題7: ポインタとスライスの実践
マンダトリー要件 (80点)
Part 1: ポインタ基礎 (20点)
ポインタを使った基本的な操作を実装してください。
ファイル: part1/pointer_basics.zig
const std = @import("std");
// TODO: 2つの値を交換
fn swap(comptime T: type, a: *T, b: *T) void {
// 実装
}
// TODO: ポインタを使って配列の要素を変更
fn multiplyByTwo(array: []i32) void {
// 実装
}
// TODO: ポインタ演算で配列の合計を計算
fn sumUsingPointer(ptr: [*]const i32, len: usize) i64 {
// 実装
}
// TODO: 配列内の最大値のポインタを返す
fn findMaxPtr(array: []i32) ?*i32 {
// 実装
}
pub fn main() void {
std.debug.print("=== Swap ===\n", .{});
var x: i32 = 10;
var y: i32 = 20;
std.debug.print("Before: x={}, y={}\n", .{x, y});
swap(i32, &x, &y);
std.debug.print("After: x={}, y={}\n", .{x, y});
std.debug.print("\n=== Multiply by Two ===\n", .{});
var numbers = [_]i32{ 1, 2, 3, 4, 5 };
std.debug.print("Before: ", .{});
for (numbers) |n| std.debug.print("{} ", .{n});
std.debug.print("\n", .{});
multiplyByTwo(&numbers);
std.debug.print("After: ", .{});
for (numbers) |n| std.debug.print("{} ", .{n});
std.debug.print("\n", .{});
std.debug.print("\n=== Sum Using Pointer ===\n", .{});
const arr = [_]i32{ 1, 2, 3, 4, 5 };
const sum = sumUsingPointer(&arr, arr.len);
std.debug.print("Sum: {}\n", .{sum});
std.debug.print("\n=== Find Max Pointer ===\n", .{});
if (findMaxPtr(&numbers)) |max_ptr| {
std.debug.print("Max value: {}\n", .{max_ptr.*});
max_ptr.* = 999;
std.debug.print("Modified: {}\n", .{numbers[numbers.len - 1]});
}
}
Part 2: スライス操作 (20点)
スライスを使った配列操作を実装してください。
ファイル: part2/slice_ops.zig
const std = @import("std");
// TODO: スライスを反転
fn reverseSlice(comptime T: type, slice: []T) void {
// 実装
}
// TODO: スライス内の要素を検索
fn findInSlice(comptime T: type, slice: []const T, target: T) ?usize {
// 実装
}
// TODO: 2つのスライスを連結(allocatorを使用)
fn concatSlices(
comptime T: type,
allocator: std.mem.Allocator,
slice1: []const T,
slice2: []const T,
) ![]T {
// 実装
}
// TODO: スライスをコピー
fn copySlice(comptime T: type, dest: []T, src: []const T) usize {
// 実装
// 戻り値: コピーした要素数
}
// TODO: スライスが等しいか判定
fn slicesEqual(comptime T: type, slice1: []const T, slice2: []const T) bool {
// 実装
}
pub fn main() !void {
const allocator = std.heap.page_allocator;
std.debug.print("=== Reverse Slice ===\n", .{});
var numbers = [_]i32{ 1, 2, 3, 4, 5 };
std.debug.print("Before: ", .{});
for (numbers) |n| std.debug.print("{} ", .{n});
std.debug.print("\n", .{});
reverseSlice(i32, &numbers);
std.debug.print("After: ", .{});
for (numbers) |n| std.debug.print("{} ", .{n});
std.debug.print("\n", .{});
std.debug.print("\n=== Find in Slice ===\n", .{});
if (findInSlice(i32, &numbers, 3)) |index| {
std.debug.print("Found 3 at index {}\n", .{index});
}
std.debug.print("\n=== Concat Slices ===\n", .{});
const arr1 = [_]i32{ 1, 2, 3 };
const arr2 = [_]i32{ 4, 5, 6 };
const concat = try concatSlices(i32, allocator, &arr1, &arr2);
defer allocator.free(concat);
std.debug.print("Concatenated: ", .{});
for (concat) |n| std.debug.print("{} ", .{n});
std.debug.print("\n", .{});
std.debug.print("\n=== Slices Equal ===\n", .{});
const eq = slicesEqual(i32, &arr1, &arr1);
std.debug.print("arr1 == arr1: {}\n", .{eq});
}
Part 3: 文字列操作 (20点)
センチネル終端文字列を使った操作を実装してください。
ファイル: part3/string_ops.zig
const std = @import("std");
// TODO: 文字列の長さを計算
fn stringLength(str: [*:0]const u8) usize {
// 実装
}
// TODO: 文字列をコピー
fn stringCopy(dest: [*]u8, src: [*:0]const u8) void {
// 実装
}
// TODO: 文字列を連結
fn stringConcat(dest: [*]u8, src1: [*:0]const u8, src2: [*:0]const u8) void {
// 実装
}
// TODO: 文字列を比較
fn stringCompare(str1: [*:0]const u8, str2: [*:0]const u8) i32 {
// 実装
// 戻り値: str1 < str2 なら負、等しいなら0、str1 > str2 なら正
}
// TODO: 文字列内で文字を検索
fn stringFindChar(str: [*:0]const u8, ch: u8) ?usize {
// 実装
}
pub fn main() void {
std.debug.print("=== String Length ===\n", .{});
const str1: [*:0]const u8 = "Hello, Zig!";
const len = stringLength(str1);
std.debug.print("Length of '{s}': {}\n", .{str1, len});
std.debug.print("\n=== String Copy ===\n", .{});
var dest: [100]u8 = undefined;
stringCopy(&dest, str1);
std.debug.print("Copied: {s}\n", .{dest[0..len]});
std.debug.print("\n=== String Concat ===\n", .{});
const src1: [*:0]const u8 = "Hello";
const src2: [*:0]const u8 = " World";
var concat: [100]u8 = undefined;
stringConcat(&concat, src1, src2);
const concat_len = stringLength(&concat);
std.debug.print("Concatenated: {s}\n", .{concat[0..concat_len]});
std.debug.print("\n=== String Compare ===\n", .{});
const cmp = stringCompare("abc", "abc");
std.debug.print("'abc' vs 'abc': {}\n", .{cmp});
std.debug.print("\n=== String Find Char ===\n", .{});
if (stringFindChar(str1, 'Z')) |index| {
std.debug.print("Found 'Z' at index {}\n", .{index});
}
}
Part 4: メモリ管理 (20点)
安全なメモリ操作を実装してください。
ファイル: part4/memory_safe.zig
const std = @import("std");
// TODO: 動的配列の作成と初期化
fn createArray(comptime T: type, allocator: std.mem.Allocator, size: usize, value: T) ![]T {
// 実装
}
// TODO: 配列のリサイズ
fn resizeArray(
comptime T: type,
allocator: std.mem.Allocator,
old_array: []T,
new_size: usize,
) ![]T {
// 実装
// 新しいサイズに配列を再割り当て
// 既存の要素をコピー
}
// TODO: 安全な配列アクセス
fn safeGet(comptime T: type, array: []const T, index: usize) ?T {
// 実装
}
// TODO: 安全な配列設定
fn safeSet(comptime T: type, array: []T, index: usize, value: T) bool {
// 実装
// 戻り値: 成功したらtrue
}
// TODO: メモリプールの実装
const MemoryPool = struct {
buffer: []u8,
offset: usize,
pub fn init(buffer: []u8) MemoryPool {
return MemoryPool{
.buffer = buffer,
.offset = 0,
};
}
pub fn alloc(self: *MemoryPool, size: usize) ?[]u8 {
// 実装
}
pub fn reset(self: *MemoryPool) void {
// 実装
}
};
pub fn main() !void {
const allocator = std.heap.page_allocator;
std.debug.print("=== Create Array ===\n", .{});
const arr = try createArray(i32, allocator, 5, 42);
defer allocator.free(arr);
std.debug.print("Array: ", .{});
for (arr) |n| std.debug.print("{} ", .{n});
std.debug.print("\n", .{});
std.debug.print("\n=== Resize Array ===\n", .{});
const resized = try resizeArray(i32, allocator, arr, 10);
defer allocator.free(resized);
std.debug.print("Resized to {} elements\n", .{resized.len});
std.debug.print("\n=== Safe Get/Set ===\n", .{});
if (safeGet(i32, arr, 2)) |value| {
std.debug.print("arr[2] = {}\n", .{value});
}
const set_ok = safeSet(i32, arr, 2, 100);
std.debug.print("Set arr[2] = 100: {}\n", .{set_ok});
std.debug.print("\n=== Memory Pool ===\n", .{});
var buffer: [1024]u8 = undefined;
var pool = MemoryPool.init(&buffer);
if (pool.alloc(100)) |mem1| {
std.debug.print("Allocated {} bytes\n", .{mem1.len});
}
if (pool.alloc(200)) |mem2| {
std.debug.print("Allocated {} bytes\n", .{mem2.len});
}
std.debug.print("Total used: {} bytes\n", .{pool.offset});
}
ボーナス課題 (20点)
Bonus 1: スマートポインタの実装 (10点)
参照カウント付きスマートポインタを実装してください。
ファイル: bonus1/smart_pointer.zig
const std = @import("std");
fn RefCounted(comptime T: type) type {
return struct {
const Self = @This();
value: T,
ref_count: usize,
allocator: std.mem.Allocator,
// TODO: 新しいインスタンスを作成
pub fn init(allocator: std.mem.Allocator, value: T) !*Self {
// 実装
}
// TODO: 参照を追加
pub fn addRef(self: *Self) void {
// 実装
}
// TODO: 参照を削除(0になったら解放)
pub fn release(self: *Self) void {
// 実装
}
};
}
pub fn main() !void {
const allocator = std.heap.page_allocator;
std.debug.print("Creating RefCounted<i32>\n", .{});
const rc1 = try RefCounted(i32).init(allocator, 42);
std.debug.print("Value: {}, RefCount: {}\n", .{rc1.value, rc1.ref_count});
rc1.addRef();
std.debug.print("After addRef: RefCount: {}\n", .{rc1.ref_count});
rc1.release();
std.debug.print("After release: RefCount: {}\n", .{rc1.ref_count});
rc1.release();
std.debug.print("Freed\n", .{});
}
Bonus 2: リングバッファの実装 (10点)
固定サイズのリングバッファを実装してください。
ファイル: bonus2/ring_buffer.zig
const std = @import("std");
fn RingBuffer(comptime T: type, comptime size: usize) type {
return struct {
const Self = @This();
buffer: [size]T,
read_pos: usize,
write_pos: usize,
count: usize,
pub fn init() Self {
return Self{
.buffer = undefined,
.read_pos = 0,
.write_pos = 0,
.count = 0,
};
}
// TODO: 要素を追加
pub fn push(self: *Self, value: T) bool {
// 実装
// バッファが満杯ならfalseを返す
}
// TODO: 要素を取り出し
pub fn pop(self: *Self) ?T {
// 実装
// バッファが空ならnullを返す
}
// TODO: 要素数を取得
pub fn len(self: Self) usize {
return self.count;
}
// TODO: バッファが空か判定
pub fn isEmpty(self: Self) bool {
return self.count == 0;
}
// TODO: バッファが満杯か判定
pub fn isFull(self: Self) bool {
return self.count == size;
}
};
}
pub fn main() void {
var buffer = RingBuffer(i32, 5).init();
std.debug.print("=== Push ===\n", .{});
_ = buffer.push(1);
_ = buffer.push(2);
_ = buffer.push(3);
std.debug.print("Pushed 3 elements, count: {}\n", .{buffer.len()});
std.debug.print("\n=== Pop ===\n", .{});
if (buffer.pop()) |value| {
std.debug.print("Popped: {}\n", .{value});
}
std.debug.print("\n=== Fill Buffer ===\n", .{});
_ = buffer.push(4);
_ = buffer.push(5);
_ = buffer.push(6);
std.debug.print("Is full: {}\n", .{buffer.isFull()});
std.debug.print("\n=== Empty Buffer ===\n", .{});
while (buffer.pop()) |value| {
std.debug.print("Popped: {}\n", .{value});
}
std.debug.print("Is empty: {}\n", .{buffer.isEmpty()});
}
評価基準
| 項目 | 配点 |
|---|---|
| Part 1: ポインタ基礎 | 20点 |
| Part 2: スライス操作 | 20点 |
| Part 3: 文字列操作 | 20点 |
| Part 4: メモリ管理 | 20点 |
| **マンダトリー合計** | **80点** |
| Bonus 1: スマートポインタの実装 | 10点 |
| Bonus 2: リングバッファの実装 | 10点 |
| **ボーナス合計** | **20点** |
合格基準
- マンダトリー: 64点以上で合格(80点満点の80%)
- ボーナス: 追加評価(最終成績の加算)
提出方法
exercise07/
├── part1/
│ └── pointer_basics.zig
├── part2/
│ └── slice_ops.zig
├── part3/
│ └── string_ops.zig
├── part4/
│ └── memory_safe.zig
├── bonus1/
│ └── smart_pointer.zig
└── bonus2/
└── ring_buffer.zig
ヒント
const temp = a.*;
a.* = b.*;
b.* = temp;
- スライスの反転:
var left: usize = 0;
var right = slice.len - 1;
while (left < right) {
// 交換
}
- センチネル終端文字列の長さ:
var len: usize = 0;
while (str[len] != 0) : (len += 1) {}
- メモリプール:
if (self.offset + size > self.buffer.len) return null;
const result = self.buffer[self.offset..self.offset + size];
self.offset += size;
参考資料
- Zig Language Reference - Pointers: https://ziglang.org/documentation/master/#Pointers
- Zig Language Reference - Slices: https://ziglang.org/documentation/master/#Slices
- Ziglearn - Pointers: https://ziglearn.org/chapter-2/#pointers
- Ziglearn - Allocators: https://ziglearn.org/chapter-2/#allocators