C言語のきほん「簡易電卓プログラム(2)改良版」

スポンサーリンク
コンピューター C言語

以前本ブログでご紹介した「簡易電卓プログラム(第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

コードのポイント

  1. グローバル変数
    • double result = 0.0; — 計算結果を保持。
    • char str[2]; — 入力された演算子またはコマンドを保持。
  2. keisan関数
    • 最初に説明文を表示する。
    • scanf(“%lf”, &result); で最初の数式(数字と演算子)を読み取り、
    • 以降はループしながら、
      • 演算子と数値を読み取り
      • 演算を実行して結果を更新
      • 計算結果を出力
      • 特別なコマンド (cやq) を処理する。 
  3. 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で即クリアどこでも cq が単独入力できる
sscanf()で柔軟な解析数式/演算子+数値両方対応
入力エラー時に再入力を促す再入力を案内する
ゼロ除算時も安全にスキップ0除算でもプログラムは止まらない

今回は、ここまでとしよう。

コメント

タイトルとURLをコピーしました