これまでの知識を総動員して、動くプログラムを作ってみよう!
C言語の基本的な文法、配列、関数、ポインタ、構造体、ファイル操作などを学んできましたね!😊 これまでのステップで得た知識は、個別の機能を作るための部品のようなものです。このステップでは、それらの部品を組み合わせて、実際に意味のある小さなプログラム(プロジェクト)を作成していきます。
自分で考えてプログラムを完成させる経験は、大きな自信と達成感につながります。ここでは、代表的な例として「電卓」と「ToDoリスト」の作成を通じて、プロジェクト開発の基本的な流れを学びましょう。
プロジェクト例1: 簡単な電卓 🧮
まずは、シンプルなコマンドライン電卓を作成してみましょう。ユーザーが「数値 演算子 数値」の形式で入力すると、計算結果を表示するプログラムです。
機能要件
- 2つの数値(整数または小数)と1つの演算子(+, -, *, /)を受け付ける。
- 入力された演算に基づいて計算を実行する。
- 計算結果を表示する。
- 基本的なエラー処理(例: 0による除算)を行う。
設計のヒント
scanf
関数を使って、2つの数値と演算子(文字として)を読み込みます。switch
文を使って、読み込んだ演算子に応じて処理を分岐させます。- 各
case
で対応する計算を実行します。 - 除算の場合は、除数が0でないかチェックする
if
文を追加します。 printf
関数を使って結果を表示します。
実装例(主要部分)
以下は、電卓の主要なロジック部分のサンプルコードです。
#include <stdio.h>
int main() {
double num1, num2, result;
char operator;
printf("計算式を入力してください (例: 10 + 5): ");
// %lf は double 型の入力、 %c は char 型の入力、間のスペースに注意
if (scanf("%lf %c %lf", &num1, &operator, &num2) != 3) {
printf("エラー: 入力形式が正しくありません。\n");
return 1; // エラー終了
}
switch (operator) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if (num2 == 0) {
printf("エラー: 0で割ることはできません。\n");
return 1; // エラー終了
}
result = num1 / num2;
break;
default:
printf("エラー: 無効な演算子です (+, -, *, / のみ有効)。\n");
return 1; // エラー終了
}
// 結果を表示 (小数点以下2桁まで表示する例)
printf("結果: %.2lf\n", result);
return 0; // 正常終了
}
- 連続して計算できるようにループ処理を追加してみましょう。
- 括弧()を含む計算に対応させてみましょう(少し難易度が上がります)。
- 三角関数(sin, cos)など、より多くの演算に対応する関数電卓を目指してみましょう。
プロジェクト例2: ToDoリスト管理ツール 📝
次によく作られる例として、やるべきタスクを管理するToDoリストがあります。タスクの追加、一覧表示、完了、削除などの機能を持つコマンドラインツールを作成します。
機能要件
- タスクを追加できる(タスクの内容を文字列で入力)。
- 現在のタスクを一覧表示できる(未完了・完了状態も表示)。
- 指定したタスクを完了状態にできる。
- 指定したタスクを削除できる。
- プログラム終了時にデータは消えても良い(まずはメモリ上での管理)。
設計のヒント
- タスクの情報を保持するための構造体を定義します。最低限、タスクID、タスク内容(文字列)、完了フラグ(真偽値や整数)が必要でしょう。
typedef struct { int id; char description[256]; // タスク内容を格納する文字列配列 int completed; // 0: 未完了, 1: 完了 } Task;
- 複数のタスクを管理するために、この構造体の配列を用意します。配列のサイズを固定するか、後で学ぶ動的メモリ確保を使うか考えます(最初は固定サイズで十分です)。
#define MAX_TASKS 100 // 最大タスク数 Task tasks[MAX_TASKS]; int task_count = 0; // 現在のタスク数
- タスクの追加、表示、完了、削除といった各機能を関数として実装すると、コードが整理されて見やすくなります(例: `addTask()`, `displayTasks()`, `markTaskComplete()`, `deleteTask()`)。
- メインループを作り、ユーザーからのコマンド(例: “add”, “list”, “done”, “delete”, “exit”)を受け付けて、対応する関数を呼び出すようにします。
- タスクIDは、タスクを追加するたびに一意になるように割り振るか、配列のインデックスをそのまま利用するなどの方法があります。
実装例(タスク追加部分)
タスクを追加する関数の簡単な例です。
#include <stdio.h>
#include <string.h> // strcpy を使うため
#define MAX_TASKS 100
#define MAX_DESC_LEN 256
typedef struct {
int id;
char description[MAX_DESC_LEN];
int completed; // 0: 未完了, 1: 完了
} Task;
Task tasks[MAX_TASKS];
int task_count = 0;
int next_id = 1; // 次に割り当てるID
// タスクを追加する関数
void addTask() {
if (task_count >= MAX_TASKS) {
printf("エラー: タスクリストがいっぱいです。\n");
return;
}
char input_desc[MAX_DESC_LEN];
printf("追加するタスクの内容を入力してください: ");
// fgets で安全に文字列を読み込む (最後の改行文字を取り除く処理も入れると良い)
// ここでは簡易的に scanf を使う例を示すが、バッファオーバーフローに注意
// scanf("%s", input_desc); // ← バッファオーバーフローの危険性あり!
// より安全な代替例 (fgets):
if (fgets(input_desc, sizeof(input_desc), stdin)) {
// fgetsは末尾に改行文字を含むことがあるので削除する
input_desc[strcspn(input_desc, "\n")] = 0;
} else {
printf("エラー: タスク内容の読み込みに失敗しました。\n");
// エラー処理: 必要に応じて入力バッファをクリアするなど
while(getchar() != '\n' && getchar() != EOF); // 簡単なバッファクリア
return;
}
// 配列の次の空き要素にタスク情報を格納
tasks[task_count].id = next_id++;
// input_desc の内容を tasks[task_count].description にコピー
strncpy(tasks[task_count].description, input_desc, MAX_DESC_LEN - 1);
tasks[task_count].description[MAX_DESC_LEN - 1] = '\0'; // 念のため終端文字を保証
tasks[task_count].completed = 0; // 未完了で追加
task_count++; // タスク数を増やす
printf("タスク「%s」(ID: %d) を追加しました。\n", input_desc, tasks[task_count - 1].id);
}
// main関数や他の関数 (displayTasks, markTaskComplete, deleteTask など) は省略
int main() {
// メインループでユーザー入力を受け付け、addTask()などを呼び出す処理
// ... (省略) ...
printf("ToDoリストへようこそ!\n");
// 例としてaddTaskを一度呼び出す
addTask();
// 他の機能呼び出しやループ処理が続く...
return 0;
}
- タスクデータをファイルに保存し、プログラムを再起動してもデータが残るようにしてみましょう(ファイル入出力の知識が必要です)。`fopen`, `fprintf`, `fscanf`, `fclose` などを使います。
- タスクに優先度(高・中・低など)や期限日を追加してみましょう。構造体の定義を変更する必要があります。
- タスクの検索機能を追加してみましょう。
- 配列ではなく、連結リストを使ってタスクを管理するように変更してみましょう(動的なデータ管理の練習になります)。
プロジェクト開発の進め方のヒント ✨
初めてプロジェクトに取り組む際に役立つヒントをいくつか紹介します。
- 小さく始める (Start Small): 最初から完璧なものを作ろうとせず、まずは最低限の機能(コア機能)だけを実装することを目指しましょう。電卓なら四則演算だけ、ToDoリストなら追加と表示だけ、など。
- 段階的に機能追加 (Incremental Development): コア機能が動いたら、少しずつ機能を追加していきます。一度に多くの変更を加えると、どこでバグが発生したのか特定しにくくなります。
- こまめにコンパイル&テスト: コードを少し書いたらコンパイルし、意図した通りに動くか簡単なテストを行います。エラーは早めに見つけて修正するのが効率的です。
- デバッグを活用する: うまく動かないときは、`printf` をコードの途中に挿入して変数の値や処理の通過点を確認する「printfデバッグ」が手軽で有効です。より本格的なデバッグには、次のステップで学ぶ `gdb` などのデバッガを使います。
- エラー処理を意識する: 予期せぬ入力(数値を入れるべきところに文字が入る、0で割ろうとするなど)があった場合に、プログラムが異常終了せず、適切にエラーメッセージを表示するように考慮しましょう。関数の戻り値でエラーを通知したり、入力値をチェックしたりします。
- 関数分割を意識する: ある程度まとまった処理は関数として切り出すことで、`main`関数がすっきりし、コードの再利用性や可読性が向上します。
- コメントを活用する: 自分自身や他の人が後でコードを読んだときに理解しやすいように、処理の意図や複雑な部分にはコメントを残しましょう。
まとめ
このステップでは、C言語の知識を組み合わせて小規模なプロジェクト(電卓、ToDoリスト)を構築する体験をしました。実際に手を動かしてプログラムを作ることで、個々の文法や機能がどのように連携して動作するのか、より深く理解できたはずです。
ここで紹介したプロジェクトはあくまで一例です。自分の興味のある題材で、小さなプログラムを自由に作ってみるのも良い練習になります。ゲーム、ユーティリティツール、データ処理プログラムなど、アイデアは無限大です!💡
次のステップ「Step 8: コンパイルとビルド」では、ソースコードが実行可能なプログラムになるまでの裏側の仕組みや、より効率的なビルド方法について学んでいきます。これにより、さらに本格的なプログラム開発への道が開かれます。頑張っていきましょう!💪
コメント