スタックはLIFO(Last In, First Out)のデータ構造であり、主な操作として初期化、解放、空判定、プッシュ(要素追加)、ポップ(要素削除)、サイズ取得、トップ要素参照が存在する。本実装では、C言語を用いて動的メモリ確保に基づくスタックを3つのファイルで構成し、安全性と拡張性を重視した設計を行う。
ヘッダーファイル:Stack.h
このファイルでは、スタックの抽象化インターフェースを定義する。構造体Stackは内部配列へのポインタ、現在の要素数(size)、および最大容量(capacity)を保持し、typedefにより型名を簡略化している。必要な標準ライブラリ(<stdio.h>, <stdlib.h>, <assert.h>, <stdbool.h>)をインクルードし、各操作関数のプロトタイプを宣言する。
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int ElemType;
typedef struct {
ElemType* buffer;
size_t size;
size_t capacity;
} Stack;
void stack_init(Stack* s);
void stack_destroy(Stack* s);
void stack_push(Stack* s, ElemType value);
void stack_pop(Stack* s);
size_t stack_length(const Stack* s);
bool stack_is_empty(const Stack* s);
ElemType stack_peek(const Stack* s);
実装ファイル:Stack.c
初期化:stack_init
構造体ポインタの妥当性を確認後、初期容量(4要素)分のヒープ領域をmallocで確保。失敗時はperrorでエラー出力し、sizeとcapacityを適切に設定する。ここでsizeは「現在の要素数」を表すため、初期値は0とする。
void stack_init(Stack* s) {
assert(s != NULL);
s->buffer = (ElemType*)malloc(4 * sizeof(ElemType));
if (s->buffer == NULL) {
perror("Failed to allocate memory for stack");
exit(EXIT_FAILURE);
}
s->size = 0;
s->capacity = 4;
}
解放:stack_destroy
確保済みのバッファ領域をfreeで解放し、ポインタをNULLに初期化。構造体メンバも明示的にゼロクリアすることで、二重解放防止とデバッグ支援を図る。
void stack_destroy(Stack* s) {
assert(s != NULL);
free(s->buffer);
s->buffer = NULL;
s->size = 0;
s->capacity = 0;
}
プッシュ:stack_push
要素追加前に容量チェックを行い、不足時はreallocで2倍に拡張。再割り当て失敗時はプログラム終了ではなく安全なエラー処理を実施。成功後、buffer[size]に値を代入し、sizeをインクリメント。
void stack_push(Stack* s, ElemType value) {
assert(s != NULL);
if (s->size == s->capacity) {
size_t new_cap = s->capacity * 2;
ElemType* new_buf = (ElemType*)realloc(s->buffer, new_cap * sizeof(ElemType));
if (new_buf == NULL) {
perror("Failed to reallocate stack memory");
exit(EXIT_FAILURE);
}
s->buffer = new_buf;
s->capacity = new_cap;
}
s->buffer[s->size] = value;
s->size++;
}
ポップ:stack_pop
空スタックに対するポップを防ぐため、事前にstack_is_emptyで検証。有効な場合のみsizeをデクリメント。要素の実際の削除は不要(論理削除)であるが、必要に応じてbuffer[size]をマスク可能。
void stack_pop(Stack* s) {
assert(s != NULL);
assert(!stack_is_empty(s));
s->size--;
}
サイズ取得:stack_length
sizeフィールドは常に有効要素数を保持するため、単純にその値を返す。
size_t stack_length(const Stack* s) {
assert(s != NULL);
return s->size;
}
空判定:stack_is_empty
size == 0を直接評価してブーリアン値を返す。安全のため、ポインタの非NULLチェックを含む。
bool stack_is_empty(const Stack* s) {
assert(s != NULL);
return s->size == 0;
}
トップ参照:stack_peek
スタックが空でないことを保証した上で、buffer[size - 1]を読み取り、値を返す。副作用のない参照操作である。
ElemType stack_peek(const Stack* s) {
assert(s != NULL);
assert(!stack_is_empty(s));
return s->buffer[s->size - 1];
}
テストファイル:main.c
実行時の検証には、典型的なシナリオ(例:5要素プッシュ→2回ポップ→サイズとトップ確認)を含む簡潔なmain関数を用いる。各関数呼び出し後に状態をprintfで出力し、動作の整合性を視覚的に確認できる。