以前本ブログでご紹介した「簡易電卓プログラム(第1版)」。
今回は、このプログラムを改良してみよう。
まずは、「簡易電卓プログラム(第1版)」を示しておこう。
//簡易電卓プログラム(第1版)
#include <stdio.h>
#include <string.h>
double result = 0.0;
char str[2];
//計算関数
double keisan(void){
double num;
printf("簡易電卓プログラム\n");
printf("四則演算ができます。(+,-,*,/)入力例-> 3+5,続けて入力する-> *8,計算結果のクリア->c,終了->q;\n");
printf(">");
if (scanf("%lf", &result) != 1)
{
printf("計算できません\n");
return 0;
}
do
{
//演算子の入力
if (scanf("%1s", str) != 1)
{
break; // 入力が正常でなければループを終了
}
if (strcmp(str, "c") == 0)
{
printf("クリアします!\n");
result = 0.0; // "c" が入力された場合クリア
keisan();
}
else if (strcmp(str, "q") == 0)
{
printf("終了します!\n");
return 0; // "q" が入力された場合、計算を終了
}
// 数値の入力
if (scanf("%lf", &num) != 1)
{
break; // 入力が正常でなければループを終了
}
switch (str[0])
{
case '+':
result += num;
break;
case '-':
result -= num;
break;
case '*':
result *= num;
break;
case '/':
if (num == 0.0)
{
printf( "ゼロでの除算はできません\n" );
return 0;
}
result /= num;
break;
default:
printf("演算記号の指定が違います\n");
return 0;
}
printf("計算結果:%g\n", result);
} while (1);
return 0;
}
int main()
{
keisan();
return 0;
}
実行結果
簡易電卓プログラム
四則演算ができます。(+,-,*,/)入力例-> 3+5,続けて入力する-> *8,計算結果のクリア->c,終了->q;
>3+4
計算結果:7
コードのポイント
- グローバル変数
- double result = 0.0; — 計算結果を保持。
- char str[2]; — 入力された演算子またはコマンドを保持。
- double result = 0.0; — 計算結果を保持。
- keisan関数
- 最初に説明文を表示する。
- scanf(“%lf”, &result); で最初の数式(数字と演算子)を読み取り、
- 以降はループしながら、
- 演算子と数値を読み取り
- 演算を実行して結果を更新
- 計算結果を出力
- 特別なコマンド (cやq) を処理する。
- 演算子と数値を読み取り
- 最初に説明文を表示する。
- main関数
keisan();を呼び出して実行する。
それでは、改良版を検討していこう。
① 演算子入力チェックが緩い
if (scanf(“%1s”, str) != 1)で演算子を判定しているが、本来なら「2文字目以降がないか」も厳密にチェックすべきであった。
✅ 改善
// 入力解析 (演算子 数値)
if (sscanf(input, "%c%lf", &op, &num) == 2) {
// 演算子が正しいかチェック
if (strchr("+-*/", op) == NULL) {
printf("無効な演算子が入力されました。\n");
continue;
}
② 異常入力後の処理が少し甘い
scanf("%lf", &num)
などでエラーが起きた場合、「エラーメッセージを出す」だけで次の入力を促しているが、たとえば「アルファベットが混ざったとき」など、バッファにゴミが残ることがある。
✅ 改善
バッファクリアだけでなく、1回だけではなく複数回ループして完全クリアする方がベターであろう。
③ 終了コードを明示しない
正常終了(return 0;
)してるが、エラー終了時(入力エラー時など)もreturn 0;
になっている。
✅ 改善
例えば「異常終了なら return 1;
」と明示するとより親切だろう。
if (scanf("%lf", &result) != 1) {
printf("数値の入力エラー。終了します。\n");
clear_input_buffer();
return 1;
}
④ ゼロ除算エラー時の動作がやや曖昧
✅ 改善(表示の工夫)
printf("エラー: 0で割ることはできません。\n");
printf("計算結果は変更されませんでした。別の演算を入力してください。\n");
⑤ コード全体に関する小さな見た目改善
- 出力メッセージをきれいにそろえると、さらにユーザーにとって使いやすくなるだろう。
- 例:計算結果出力後に
"------------------------------------"
を表示して区切る。
✅ 改善
printf("現在の計算結果: %g\n", result);
printf("------------------------------------\n");
それでは、改良版の全コードを表示しておこう。
// 簡易電卓プログラム(改良版)
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define STR_SIZE 100
// 入力バッファクリア関数
void clear_input_buffer(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF) {}
}
// 初期メッセージ表示関数
void print_intro(void) {
printf("\n--- 簡易電卓プログラム ---\n");
printf("四則演算ができます。(+,-,*,/)\n");
printf("入力例 -> 3+5\n");
printf("続けて入力する -> *8\n");
printf("計算結果のクリア -> c\n");
printf("終了 -> q\n");
printf("------------------------------\n");
}
int main(void) {
double result = 0.0;
double num = 0.0;
char input[STR_SIZE];
char op;
int first_input = 1;
print_intro();
while (1) {
if (first_input) {
printf("最初の数式を入力してください (例: 3+5): ");
if (fgets(input, sizeof(input), stdin) == NULL) {
printf("入力エラー。終了します。\n");
break;
}
// 入力が "q" または "c" だったらコマンド処理
if (strcmp(input, "q\n") == 0) {
printf("プログラムを終了します!\n");
break;
}
if (strcmp(input, "c\n") == 0) {
printf("クリアします!\n");
result = 0.0;
first_input = 1;
print_intro();
continue;
}
// 入力解析 (数値 演算子 数値)
if (sscanf(input, "%lf%c%lf", &result, &op, &num) == 3) {
switch (op) {
case '+': result += num; break;
case '-': result -= num; break;
case '*': result *= num; break;
case '/':
if (num == 0.0) {
printf("エラー: 0で割ることはできません。\n");
result = 0.0;
first_input = 1;
continue;
}
result /= num;
break;
default:
printf("無効な演算子が入力されました。\n");
result = 0.0;
first_input = 1;
continue;
}
printf("現在の計算結果: %g\n", result);
printf("------------------------------\n");
first_input = 0;
} else {
printf("無効な入力です。再入力してください。\n");
continue;
}
} else {
printf("続けて演算子と数値を入力してください (例: *8), c(クリア), q(終了): ");
if (fgets(input, sizeof(input), stdin) == NULL) {
printf("入力エラー。終了します。\n");
break;
}
// "q" や "c" を単独入力した場合の処理
if (strcmp(input, "q\n") == 0) {
printf("プログラムを終了します!\n");
break;
}
if (strcmp(input, "c\n") == 0) {
printf("クリアします!\n");
result = 0.0;
first_input = 1;
print_intro();
continue;
}
// 入力解析 (演算子 数値)
if (sscanf(input, "%c%lf", &op, &num) == 2) {
// 演算子が正しいかチェック
if (strchr("+-*/", op) == NULL) {
printf("無効な演算子が入力されました。\n");
continue;
}
switch (op) {
case '+': result += num; break;
case '-': result -= num; break;
case '*': result *= num; break;
case '/':
if (num == 0.0) {
printf("エラー: 0で割ることはできません。\n");
printf("計算結果は変更されませんでした。別の演算を入力してください。\n");
continue;
}
result /= num;
break;
}
printf("現在の計算結果: %g\n", result);
printf("------------------------------\n");
} else {
printf("無効な入力です。再入力してください。\n");
continue;
}
}
}
return 0;
}
実行結果
--- 簡易電卓プログラム ---
四則演算ができます。(+,-,*,/)
入力例 -> 3+5
続けて入力する -> *8
計算結果のクリア -> c
終了 -> q
------------------------------
最初の数式を入力してください (例: 3+5): 3+5
現在の計算結果: 8
------------------------------
続けて演算子と数値を入力してください (例: *8), c(クリア), q(終了): *8
現在の計算結果: 64
------------------------------
続けて演算子と数値を入力してください (例: *8), c(クリア), q(終了): /6
現在の計算結果: 10.6667
------------------------------
続けて演算子と数値を入力してください (例: *8), c(クリア), q(終了): q
プログラムを終了します!
改良版まとめ
ポイント | 内容 |
---|---|
fgets() で一括入力読取 | 行単位で入力を受け取るのでc/qの判定ができる |
q で即終了、c で即クリア | どこでも c 、q が単独入力できる |
sscanf() で柔軟な解析 | 数式/演算子+数値両方対応 |
入力エラー時に再入力を促す | 再入力を案内する |
ゼロ除算時も安全にスキップ | 0除算でもプログラムは止まらない |
今回は、ここまでとしよう。
コメント