中間テストはペーパーテストが 12 点満点(順に 2, 3, 2, 2, 3 点)、
オンラインテストが 8 点満点(4, 4 点)で採点する。
(ただし、最終的な成績の計算にはこの半分 — ペーパーテスト 6点満点、
オンラインテスト 4 点満点 — の割合で用いる。)
期末テストは、もっと難易度は高くなる。
今回はテストであるので、NG というメッセージがあっても、 再提出する必要はない。
ペーパーとオンラインを合算して、得点率が 6 割未満の者には、 追試験を実施する予定である。詳細は追って連絡する。(nov21_1.c へのメッセージにペーパーテスト・オンラインテストの得点を通知する予定である。
オンラインテストでセミコロンの付け忘れなど基本的な文法事項で何度もエラーを出しているようでは、 時間内に課題を仕上げることが非常に困難になる。 文法は“何となく”ではなく、確実に理解しておく必要がある。
以下であげる平均点などの統計値は暫定値であり、見直しの可能性はある。
以下のプログラムは動作はするがインデンテーションが滅茶苦茶でプログラムの構造を正しく反映していない。 (そして、おそらく作成者の意図したようには動かない。) プログラムの構造を明確にするために「プログラミング」のインデンテーションの約束事(別紙)に厳密に従って(特に Ⅴ 番目のルールに注意)、必要に応じてブレースを挿入し、インデントしなおせ。 ただし、ブレース内の字下げは紙幅の制限のため 3文字分とせよ。
もちろん、プログラムの意味(実行結果)が変わらないようにすること。 (つまり、作成者の(よくわからない)意図を忖度するのではなく、 コンパイラーが解釈する通りにインデントすること。
解答は、最初の #include <stdio.h> は省略せよ。また、 解答欄のスペースの都合で、(最後は別として)空行は入れずに記入せよ。
int main(void) { int i, j; for (i = 0; i < 3; i++) for (j = 0; j < 4; j++) if (j % 2 == 0) putchar('@'); else if (i % 3 == 0) putchar('*'); else putchar('#'); if (j % 3 == 1) putchar('\n'); return 0; }
正解は以下の通りである。
int main(void) { int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { if (j % 2 == 0) { putchar('@'); } else if (i % 3 == 0) { putchar('*'); } else { putchar('#'); } } } if (j % 3 == 1) { putchar('\n'); } return 0; }
正解したのは 28 名で、 32.2 % (28/87) だった。 部分点を与えた解答を含めても 36 名 (41.4 %)だった。 問1 の正解/不正解と平均点の関係は以下の通りである。
問1 | ペーパー(問1以外) | オンライン |
---|---|---|
2点 | 7.96 / 10 | 5.76 / 8 (72.0 %) |
1点 | 6.50 / 10 | 3.55 / 8 (44.4 %) |
0点 | 6.85 / 10 | 3.69 / 8 (46.1 %) |
正解したひととそれ以外でオンライン試験の得点に大きな差が出ている。
インデンテーションがちゃんとできない人は、 肝心のプログラムの構造がわかっていない、勘でプログラムを作っている、 と判断せざるをえない。教科書を読み返して、C 言語の文法を充分に理解しておくこと。
プログラミング言語の文法は、日本語や英語などの自然言語の文法に比べてはるかに単純である。 理解せずに勘でプログラムを書こうとするのは、時間の浪費で、今後の進歩が望めない。
予想外に多かったのが次のような誤答である。
/* 大間違い */ int main(void) { int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { if (j % 2 == 0) { putchar('@'); } } } else if (i % 3 == 0) { putchar('*'); } else { putchar('#'); } if (j % 3 == 1) { putchar('\n'); } return 0; }
そもそも、else から始まる C 言語の文はない。また for 〜 else というかたちの文もない。 つまり else は必ず前の if とくっついて文になる必要がある。
プログラムの意味(実行結果)を変えてしまうようにブレースを入れてしまう誤答も とても多い。if や for の対象となるのは、1 つの文だから、 (スペースや改行の位置は別にして、)ブレースは以下の位置に入る。
int main(void) { int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { if (j % 2 == 0){putchar('@');} else if (i % 3 == 0){putchar('*');} else{putchar('#');}}} if (j % 3 == 1){putchar('\n');} return 0; }
教科書の p.43, p.44, p.46, pp.58-59, p.72-73, p.80, pp.90-91 の図を確認しておくこと。
例えば、以下のようにブレースをいれてしまうと、プログラムの意味が変わってしまう。
/* 間違い */ int main(void) { int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { if (j % 2 == 0) { putchar('@'); } else if (i % 3 == 0) { putchar('*'); } else { putchar('#'); } } if (j % 3 == 1) { putchar('\n'); } } return 0; }
/* 間違い */ int main(void) { int i, j; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { if (j % 2 == 0) { putchar('@'); } else if (i % 3 == 0) { putchar('*'); } else { putchar('#'); } if (j % 3 == 1) { putchar('\n'); } } } return 0; }
開発チームの他の人や 3 ヵ月後の自分がソースを読んだときに、 このような勘違いが起きないよう、インデンテーションの約束事がある。 特に:
ブレース({ 〜 })の中は、外よりも 4(ただし、今回のテストでは 3) 字分(首尾一貫した文字数)を字下げする。
if 文, if 〜 else 文 や for 文, while 文, do 〜 while 文などでは、 選択されたり、繰り返したりされる文が一つだけの場合も、 ブレース({〜})に囲む。
は徹底する必要がある。
なお、このようなブレースを追加しても、 コンパイルされた結果である実行可能ファイルの効率が悪くなる心配はない。
次のプログラムは一つの実数値を読み込んで、 それを 3 倍した値と、10 倍して(切り捨てて)整数に変換した値を表示するプログラムである。
たとえば、3.31 を入力すると、
それを 3 倍した値は 9.930000 です。 それを 10 倍して切り捨てた値は 33 です。
と出力する。 空欄を埋めて、プログラムを完成せよ。
int main(void) { double num; printf("実数を入力して下さい: "); scanf("", ); printf("それを 3 倍した値は です。\n", ); printf("それを 10 倍して切り捨てた値は です。\n", ); return 0; }
正解(例)とコメントは以下のとおりである。
番号 | 正解例 | コメント |
---|---|---|
(ⅰ) | %lf | %f がやや多かった。 |
(ⅱ) | &num | & の付け忘れがやや目立った。また、手書きのとき & が裏返しになっている人が多い。 |
(ⅲ) | %f | |
(ⅳ) | num * 3 | |
(ⅴ) | %d | %.0f は小数第一位を四捨五入した double の値を表示するので、題意と違う。 |
(ⅵ) | (int)(num * 10) | この問題の正答率は低かった。(int)num * 10 では切り捨ててから 10 倍することになる。 int(num * 10) という間違いも多かった。C 言語にはこの書き方はない。 |
次のプログラムの出力を書け。
int main(void) { int k = 30; if (k % 6 == 0) { printf("A "); } if (k % 2 == 0) { printf("B "); } else if (k % 3 == 0) { printf("C "); } if (k % 5 == 0) { printf("D "); } printf("\n"); return 0; }
正解は、「A B D 」。
k % 2 == 0 という条件が成り立つので、else 部の if (k % 3 == 0) { … } は評価されない。つまり C は出力されない。 if (k % 5 == 0) { … } は、else 部ではなく、 次の文なので評価される。
問3 | ペーパー(問3以外) | オンライン |
---|---|---|
正解 | 6.3 / 10 | 4.48 / 8 (56.0 %) |
不正解 | 3.5 / 10 | 2.8 / 8 (35.0 %) |
以下の空欄を埋めて、 1 以上 15 以下 の整数のみを入力として受付け、 範囲外の整数が入力された場合は読込みを繰り返すプログラムを完成させよ。
int main(void) {
int n;
do {
printf("1 〜 15 の整数を入力してください: ");
scanf("%d", &n);
} while ( );
printf("徳川幕府第 %d 代将軍は、", n);
switch (n) {
case 1: printf("家康"); break;
case 2: printf("秀忠"); break;
case 3: printf("家光"); break;
case 4: printf("家綱"); break;
case 5: printf("綱吉"); break;
/* 途中略 */
case 15: printf("慶喜"); break;
}
printf("です。\n");
return 0;
}
正解例は、「n < 1 || 15 < n」。
「n ≦ 0」という解答があったが、「≦」という演算子は C 言語にはないので採点に困る。
「1 <= n && n <= 15」という誤答が多かった。 do〜while 文の条件式は真のときに繰返しが続くので、これでは正しい入力のときに、再度入力を促すことになる。
「||」「&&」を使った間違いはまだ良いが、「,」、「;」などを使った人は、 このページを読んでおくこと。
int main(void) { int i, j, n = 4; for (i = 1; i <= n; i++) { for (j = i; j < 2 * i; j++) { printf("(%d,%d) ", i, j); } printf("\n"); } return 0; }
正解は以下の通りである。
(1,1)␣↓ (2,2)␣(2,3)␣↓ (3,3)␣(3,4)␣(3,5)␣↓ (4,4)␣(4,5)␣(4,6)␣(4,7)␣↓
最後にもう一列付け足してしまう次のような誤りがやや多かった。
(1,1)␣(1,2)␣↓ (2,2)␣(2,3)␣(2,4)␣↓ (3,3)␣(3,4)␣(3,5)␣(3,6)␣↓ (4,4)␣(4,5)␣(4,6)␣(4,7)␣(4,8)␣↓
「for (j = i; j < 2 * i; i++) …」という形は j が 2 * i になったときには繰返しを抜けることに注意する。
正の整数を入力してください: 4↵
* 3 6 9
実行例 2:
正の整数を入力してください: 9↵
* 3 6 9 * 15 18 21 *
実行例 3:
正の整数を入力してください: -1↵
実行例 4:
正の整数を入力してください: 1↵
*
正解例は以下のとおりである。
#include <stdio.h> int main(void) { int i, n; printf("正の整数を入力してください: "); scanf("%d", &n); for (i = 0; i < n; i++) { int k = i * 3; if (k % 4 == 0) { printf("* "); } else { printf("%d ", k); } } putchar('\n'); return 0; }
平均点は 2.6 点(65.0 %)である。
初項(0)を特別扱いする必要はないのに、ループの外側で if 文などを使っている解答が多い。 できるだけシンプルなプログラムを心掛けるようにする。
「4 の倍数は数を表示するかわりに」という部分が実装できておらず、 4 の倍数を表示した前か後に「*」を表示する解答も多かった。
「n 回繰り返す」(for (i = 0; i < n; i++) または、 for (i = 1; i <= n; i++) )というのは繰返しの基本の形だが、 それができていない解答が多い。
やはりインデンテーションが約束事にそってできていない解答が多い。 この問題の場合はインデンテーションの誤りは 10 〜 20 % の減点とする。
下に示すように、正の整数 n を受け取ってディスプレイ上に、 1 行目が * を n 個、 2 行目が * を n - 2 個、 3 行目が * を n - 4 個、 というように*を繰り返し、 横の辺の長さが n、縦の辺の長さが n / 2(n が偶数のとき)、 (n + 1) / 2 (n が奇数のとき)の直角三角形を表示するプログラムを作成せよ。 (n として負の数または 0 を受け取ったときは、何も表示しなくてよい。)
実行例 1:
正の整数を入力してください: 4↵
****
**
実行例 2:
正の整数を入力してください: 13↵
*************
***********
*********
*******
*****
***
*
実行例 2:
正の整数を入力してください: 0↵
正解例は以下のとおりである。
#include <stdio.h> int main(void) { int i, j, n; printf("正の整数を入力してください: "); scanf("%d", &n); for (i = n; i > 0; i -= 2) { for (j = 0; j < i; j++) { putchar('*'); } putchar('\n'); } return 0; }
平均点は 1.7 点(42.5 %)である。
外側のループの回数が多過ぎるという誤りが多い。
奇数と偶数で処理をわける必要はないのに、ループの外側で if 文などを使っている解答が多い。 やはりできるだけシンプルなプログラムを心掛けるようにする。