Cygwinでデバッグ

例題

例題を思いつくまま書き連ねてみます。

  1. #include <stdio.h>
    の "stdio" の読み方を記せ。
  2. 以下のプログラムを読んで、次の問に答えよ。
    #include <stdio.h>
    
    int five_times(int x) {  /* (1) */
        x *= 5;              /* (2) */
        return x;            /* (3) */
    }
    
    int main(void) {
        int x = 5;                      /* (4) */
        int y = 10;                     /* (5) */
        
        printf("%d\n", five_times(x));  /* (6) */
        printf("%d\n", five_times(x));  /* (7) */
        printf("%d\n", five_times(y));  /* (8) */
        printf("%d\n", five_times(y));  /* (9) */
        return 0;
    }
    1. (3) で返される x の変数と同じものは (1)〜(2), (4), (6), (7) のうちどれであるか。
    2. (2) で x の値は5倍になるが、(6) と (7) の表示は同じになるか。
    3. (2) で x の値は5倍になるが、(8) と (9) の表示は同じになるか、それともコンパイルエラーになるか。

問題分割の方法(1)

底辺 b, 高さ h の三角形を考える。ただし b, h ともに整数で、10≦b≦20, 10≦h≦20 の範囲をとるものとする。この中で、面積が 70 以上 100 以下になるものの数を数えたい。次の指示に従って、プログラムを完成させなさい。

  1. 底辺 b, 高さ h の三角形の面積を返す関数 double triangle(int b, int h) を作りなさい。
  2. main() 関数で、底辺5, 高さ5 の三角形の面積を、関数 triangle() を用いて計算し、printf() で表示しなさい。正当な値が表示されることも合わせて確認しなさい。
  3. main() 関数を作り直して、底辺 10〜20, 高さ 10〜20 の範囲の三角形の面積をすべて表示するようにしなさい。for ループが2重の入れ子になることを期待している。
  4. (内側の)ループの中に if 文を追加して、三角形の面積が 70〜100 の場合のみ、表示するように変更しなさい。
  5. 数を数えるための変数 int count を main() 関数の中で宣言し、これを利用して三角形の面積が 70〜100 となるものの数を数えなさい。
  6. main() 関数の最後で「条件を満たす三角形は x 個あります。」と表示しなさい。
  7. ループ内の printf() をコメントにしなさい。消さずにコメントで残しておくのがよい。

    ねらい

    • 複雑な動作をするプログラムを、一気に完成させるのは難しい。
    • 効率よくプログラムを作るには、小さなパーツに分解して、計算結果を表示させるなどして、少しずつ動作を確認しながら完成させるとよい。
    • しかし、どのように問題を分解して、小さなパーツに分ければよいのか、一口に説明するのは難しい。上記のような誘導がひとつの参考になる。
    • 三角形の面積計算を関数に独立させる有難みがわからない人は、a と b の最大公約数が 10〜20 になるような (a, b) の組みの数を求めるプログラムを作ってみるとよい。

問題分割の方法(2)

sin(x)の小数第1位と、tan(x)の小数第1位が、同じ値になるような角度xを見つけたい。ただしxは度数法で表した整数値であり、0≦x≦360 の範囲で探すものとする。以下の誘導に従ってプログラムを作成せよ。

  1. 角度をラジアンに変換する関数 double deg2rad(double deg) を作れ。なお、円周率は <math.h> に定義されている M_PI というマクロを用いるとよい。
  2. 角度を引数にとって正弦を返す関数 double sin_deg(double deg) を作れ。同様に、余弦を返す関数 double cos_deg(double deg) と正接を返す関数 double tan_deg(double deg) も作れ。ただし、ラジアンへの変換は 1.で作った deg2rad() 関数を利用すること。
  3. 小数点第1位を返す関数 int first_decimal_place(double x) を作れ。
  4. 以下の main() 関数で、これまでに作った関数の動作を確認せよ。
    #include <stdio.h>
    #include <math.h>
    
    double deg2rad(double deg) { _____________; } // 1.で作成
    double sin_deg(double deg) { _____________; } // 2.で作成
    double cos_deg(double deg) { _____________; } // 2.で作成
    double tan_deg(double deg) { _____________; } // 2.で作成
    
    int first_decimal_place(double x) // 3.で作成
    {
        _____________;
        _____________;
        _____________;
        _____________;
    }
    
    int main(void)
    {
        int i;
     
        printf("角度をラジアンに変換する関数 deg2rad() の動作を確認します。\n");
        for (i=0; i<=180; i+=30) {
            printf("%3d %f\n", i, deg2rad(i));
        }
        printf("\n");
    
        printf("sin_deg() 関数等の動作を確認します。\n");
        printf("x\tsin(x)\t\tcos(x)\t\ttan(x)\n");
        for (i=0; i<=180; i+=30) {
            printf("%3d\t%f\t%f\t%f\n", i, sin_deg(i), cos_deg(i), tan_deg(i));
        }
        printf("\n");
    
        printf("小数第1位を取り出す関数 first_decimal_place() の動作を確認します。\n");
        for (i=0; i<20; i++) {
           double a = (i-10)*0.31;
           printf("%f %d\n", a, first_decimal_place(a));
        }
        printf("\n");
    
        /* 
        printf("sin(x) と tan(x) の小数第1位が等しくなる角度は以下の通りです。\n");
        printf("x\tsin(x)\t\ttan(x)\n");
        for (i=0; i<=360; i++) {
            _____________;
            _____________;
            _____________;
            if (_____________) {
                printf("%d\t%f\t%f\n", _____________);
            }
        }
        */
        return 0;
    }
    出力は以下のようになる。
    角度をラジアンに変換する関数 deg2rad() の動作を確認します。
      0 0.000000
     30 0.523599
     60 1.047198
     90 1.570796
    120 2.094395
    150 2.617994
    180 3.141593
    
    sin_deg() 関数等の動作を確認します。
    x	sin(x)		cos(x)		tan(x)
      0	0.000000	1.000000	0.000000
     30	0.500000	0.866025	0.577350
     60	0.866025	0.500000	1.732051
     90	1.000000	0.000000	16331239353195370.000000
    120	0.866025	-0.500000	-1.732051
    150	0.500000	-0.866025	-0.577350
    180	0.000000	-1.000000	-0.000000
    
    小数第1位を取り出す関数 first_decimal_place() の動作を確認します。
    -3.100000 1
    -2.790000 7
    -2.480000 4
    -2.170000 1
    -1.860000 8
    -1.550000 5
    -1.240000 2
    -0.930000 9
    -0.620000 6
    -0.310000 3
    0.000000 0
    0.310000 3
    0.620000 6
    0.930000 9
    1.240000 2
    1.550000 5
    1.860000 8
    2.170000 1
    2.480000 4
    2.790000 7
  5. 角度x(ただしxは0以上360以下の整数)に関して、sin(x)の小数第1位と、tan(x)の小数第1位が、同じ値になるものをすべて表示しなさい。出力は以下のようになる。
    sin(x) と tan(x) の小数第1位が等しくなる角度は以下の通りです。
    x	sin(x)		tan(x)
    0	0.000000	0.000000
    1	0.017452	0.017455
    2	0.034899	0.034921
    ...(略)
    109	0.945519	-2.904211
    ...(略)
    200	-0.342020	0.363970
    ...(略)
    299	-0.874620	-1.804048
    330	-0.500000	-0.577350
    ...(略)
    360	-0.000000	-0.000000

    ねらい

    • first_decimal_place() 関数を単独で動作確認しておくことが、後の作業の効率化につながっている。このことを実感してほしい。sin_deg() などと組み合わせた状態では、first_decimal_place() 関数の動作を網羅的に確認することは難しい。
    • sin_deg() と tan_deg() でラジアン変換の処理が共通化されていることにも着目して欲しい。同じ作業を共通化することで、間違いがあれば発見しやすくなり、信頼性を高めることにつながっている。
    • cos_deg() 関数は動作確認以外に用いられていないが、sin_deg() と同時に作成しておくとよい。必要になってから作るよりも手間が少なくてすむ。

位取り

次のような場合の時刻を表示せよ。時刻は 12:34:56 のような形式(24時間制)で表示するとする。(解答例は filehms.c

  1. (例) ある時計台では、7時〜21時の間、毎時0分に時報が鳴る。時報の鳴る時刻を表示せよ。
    • 出力例
       7:00:00
       8:00:00
       9:00:00
      10:00:00
      ...
      20:00:00
      21:00:00
    • プログラム例
      int i;
      for (i=7; i<=21; i++) {
          printf("%2d:00:00\n", i);
      }
  2. ある駅では、7時から21時30分まで、30分おきに電車が出発する。電車が出発する時刻を表示せよ。
    • 出力例
       7:00:00
       7:30:00
       8:00:00
       8:30:00
      ...
      21:00:00
      21:30:00
  3. ある駅では、7時から、30分おきに1本ずつ、合計14本の電車が出発する。電車が出発する時刻を表示せよ。
  4. ある駅では、7時から、30分おきに1本ずつ、合計15本の電車が出発する。電車が出発する時刻を表示せよ。
    • プログラムのヒント(途中まで)
         int i, h=7, m=0, s=0;
      
         for (i=0; i<15; i++) {
             printf("%2d:%02d:%02d\n", h, m, s);
             m = m + 30;
             ...
  5. ある駅では、7時から、35分おきに1本ずつ、合計15本の電車が出発する。電車が出発する時刻を表示せよ。
  6. ある駅では、7時から、65分おきに1本ずつ、合計15本の電車が出発する。電車が出発する時刻を表示せよ。
  7. ある駅では、7時から、65分おきに1本ずつ、合計20本の電車が出発する。電車が出発する時刻を表示せよ。

     

  8. 深夜の宿直の交代は 20時から始まって3時間ごとで、翌朝8時まで続く。交代の時刻を表示せよ。
  9. 20時から翌朝8時までの宿直の間、50分ごとに冷蔵庫の庫内温度を記録する。記録すべき時刻を出力せよ。
    • 出力例
      20:00:00
      20:50:00
      21:40:00
      ....
  10. 20時から翌朝8時までの宿直の間、50分ごとに冷蔵庫の庫内温度を記録する。50分ごとにアラームの鳴る時計を利用したが、精度不良のため、実際には49分51秒ごとに記録していた。記録していた時刻を出力せよ。
    • 出力例
      20:00:00
      20:49:51
      21:39:42
      ....

if/else の使い方と return の使い方

ある通信会社のデータ通信の毎月の料金プランは次のようになっている。

  1. 契約をした時点で1000円の基本料金が発生する。また、1000MB まで追加料金なしで通信できる。
  2. 通信量が 1000MB を超えると、超えた通信量に対して1MB あたり2円の従量料金が加算される。
  3. ただし、通信量が 3000MB を超えると、基本料金を含めて5000円の定額となる。

通信量 x(MB) に対する課金額を計算する関数を次の2通りの処理方法で作りなさい。

なお、x として負の通信量が与えられた場合は、 エラーを表すために -1 を返しなさい。

main関数では、0MB, 1000MB, 3000MB のような、条件の境界となる通信量に対して、関数の値が正しいか確認しなさい。また、-1000MB〜5000MBの通信量に対して、fee1(), fee2() が同じ値を返すことも確認しなさい。

このように、関数1つを取り出して動作チェックを行う手法を「ユニットテスト」と言う。

考察せよ

次の2つのプログラムの違いを考察せよ。

間違いを直せ


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS