0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

この記事について

「C言語の基礎を学ぼう」をテーマに、自身の知識 + α をアドベントカレンダーにまとめます。
25日間でC言語をマスターしよう - Qiita Advent Calendar 2025 - Qiita

こんな方を対象としています

  • コンピュータがプログラムをどのように動かしているか知りたい/知らない方

  • プログラミングをしてみたい方

  • C言語初心者の方

キーワード

  • スタック

  • キュー

説明

スタック

データの扱い方の形式のことを データ構造 といいます。
データ構造のひとつに スタック があります。

スタックは複数のデータを格納する箱のようなものです。
後に入れられたデータほど先に出す という LIFO (Last In First Out) の特徴があります。

スタックにデータを入れることを プッシュ (push) 、スタックからデータを取り出すことを ポップ (pop) といいます。

下記のような動作になります。

push(5);
pop(); // -> 5
push(1);
push(2);
push(3);
pop(); // -> 3
pop(); // -> 2

C言語での実装は 練習 に記載します!

キュー

スタックと対比されるデータ構造に キュー があります。

キューも複数のデータを格納する箱のようなものです。
先に入れられたデータほど先に出す という FIFO (First In First Out) の特徴があります。

キューにデータを入れることを エンキュー (enqueue) 、キューからデータを取り出すことを デキュー (dequeue) といいます。

下記のような動作になります。

enqueue(5);
dequeue(); // -> 5
enqueue(1);
enqueue(2);
enqueue(3);
dequeue(); // -> 1
dequeue(); // -> 2

C言語での実装は 練習 に記載します!

練習

1. 「戻る」を実装しよう

ブラウザやエディターなどに実装されている「戻る」ボタンの動作を行うプログラムを作成しよう。

操作名を入力(0:戻る,1:終了):ボタンの色を赤にする
実行しました。
操作名を入力(0:戻る,1:終了):枠線を青にする
実行しました。
操作名を入力(0:戻る,1:終了):0
「枠線を青にする」を元に戻しました。
操作名を入力(0:戻る,1:終了):0
「ボタンの色を赤にする」を元に戻しました。
操作名を入力(0:戻る,1:終了):0
元に戻す動作がありません。
操作名を入力(0:戻る,1:終了):1
終了しました。

ポイント

スタックの動作です。スタックを実装しましょう。
メモリ確保でも実装できますが、簡素化のためスタック本体は配列を利用します。
要素数が0のとき、ポップできない点に注意しましょう。
スタックのサイズを超えてプッシュできない処理も必要ではありますが、今回は割愛します。

解答例

#include <stdio.h>
#include <string.h>

#define MAX_STACK_SIZE 64
#define MAX_STR_LENGTH 256

char stack[MAX_STACK_SIZE][MAX_STR_LENGTH];
int stack_el_num = 0;

void push(char *str);
char *pop(void);

int main(void) {
    char input_str[MAX_STR_LENGTH];
    char *pop_data = NULL;

    while(1) {
        printf("操作名を入力(0:戻る,1:終了):");
        scanf("%s",input_str);

        if (input_str[0] == '1' && input_str[1] == '\0') {
            // 処理終了
            printf("終了しました。\n");
            break;
        } else if (input_str[0] == '0' && input_str[1] == '\0') {
            // 「戻る」の動作
            pop_data = pop();
            if (pop_data == NULL) {
                printf("元に戻す動作がありません。\n");
            } else {
                printf("「%s」を元に戻しました。\n", pop_data);
            }
        } else {
            printf("実行しました。\n");
            // 操作を記憶する
            push(input_str);
        }
    }

    return 0;
}

void push(char *str) {
    strcpy(stack[stack_el_num++], str);
}

char *pop(void) {
    if (stack_el_num == 0) return NULL;
    return stack[--stack_el_num];
}
操作名を入力(0:戻る,1:終了):ボタンの色を赤にする
実行しました。
操作名を入力(0:戻る,1:終了):枠線を青にする
実行しました。
操作名を入力(0:戻る,1:終了):0
「枠線を青にする」を元に戻しました。
操作名を入力(0:戻る,1:終了):0
「ボタンの色を赤にする」を元に戻しました。
操作名を入力(0:戻る,1:終了):0
元に戻す動作がありません。
操作名を入力(0:戻る,1:終了):1
終了しました。

2. レストランの「順番待ち」を実装しよう

「名前を書く」動作と「お客さんを呼ぶ」動作を実装しよう。

名前を入力(0:お客さんを呼ぶ,1:終了):田中
書きました。
名前を入力(0:お客さんを呼ぶ,1:終了):佐藤
書きました。
名前を入力(0:お客さんを呼ぶ,1:終了):0
「田中」様、お待たせいたしました。
名前を入力(0:お客さんを呼ぶ,1:終了):0
「佐藤」様、お待たせいたしました。
名前を入力(0:お客さんを呼ぶ,1:終了):0
待っている方はいません。
名前を入力(0:お客さんを呼ぶ,1:終了):1
終了しました。

ポイント

キューの動作です。キューを実装しましょう。
メモリ確保でも実装できますが、簡素化のためキュー本体は配列を利用します。
要素数が0のとき、デキューできない点に注意しましょう。
キューの範囲を超えてエンキューできない処理も必要ではありますが、今回は割愛します。
スタックと異なり、エンキューする場所とデキューする場所、それぞれを記憶する必要があります。

解答例

#include <stdio.h>
#include <string.h>

#define MAX_QUEUE_SIZE 64
#define MAX_STR_LENGTH 256

char queue[MAX_QUEUE_SIZE][MAX_STR_LENGTH];
int queue_el_start = 0;
int queue_el_end = 0;

void enqueue(char *str);
char *dequeue(void);

int main(void) {
    char input_str[MAX_STR_LENGTH];
    char *dequeue_data = NULL;

    while(1) {
        printf("名前を入力(0:お客さんを呼ぶ,1:終了):");
        scanf("%s",input_str);

        if (input_str[0] == '1' && input_str[1] == '\0') {
            // 処理終了
            printf("終了しました。\n");
            break;
        } else if (input_str[0] == '0' && input_str[1] == '\0') {
            // お客様を呼ぶ
            dequeue_data = dequeue();
            if (dequeue_data == NULL) {
                printf("待っている方はいません。\n");
            } else {
                printf("「%s」様、お待たせいたしました。\n", dequeue_data);
            }
        } else {
            printf("書きました。\n");
            // 名前を記憶する
            enqueue(input_str);
        }
    }

    return 0;
}

void enqueue(char *str) {
    strcpy(queue[queue_el_end++], str);
}

char *dequeue(void) {
    if (queue_el_start == queue_el_end) return NULL;
    return queue[queue_el_start++];
}
名前を入力(0:お客さんを呼ぶ,1:終了):田中
書きました。
名前を入力(0:お客さんを呼ぶ,1:終了):佐藤
書きました。
名前を入力(0:お客さんを呼ぶ,1:終了):0
「田中」様、お待たせいたしました。
名前を入力(0:お客さんを呼ぶ,1:終了):0
「佐藤」様、お待たせいたしました。
名前を入力(0:お客さんを呼ぶ,1:終了):0
待っている方はいません。
名前を入力(0:お客さんを呼ぶ,1:終了):1
終了しました。

エンキュー、デキューを繰り返すと配列の終端に近づいてしまいます。
長期的に利用する場合、キューの始点と終点をつなげた リングバッファ が使用されることが多いです。

おわりに

データ構造を知ることはアルゴリズムを知ることにもつながります。
練習に記載したような身近なシステムをデータ構造でスマートに書きたいですね。

参考文献

↓↓↓ はじめてプログラミングを学んだときに読んだ本です ↓↓↓
詳細(プログラミング入門 C言語)|プログラミング|情報|実教出版

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?