前期試験 ・ 2018 年 7 月 26 日 4 時限実施 ・ ページ
| 授業 科目 |
計算機実習 I | 学生番号 | 学科 | 学年 | 組 | 番 | フリ ガナ |
評点 | ||||||||
| 氏名 | ||||||||||||||||
| 担当者 | DÜRST, Martin J.、莊司、 高野、豊田、横窪 |
|||||||||||||||
下記の表の「書式」、「式1」、「式2」を使って、 printf(書式, 式1);
または printf(書式, 式1, 式2); の文が出力する結果を文字別に表に記入しなさい。スペースは
SP、小数点は DOT と書く。'a' の文字コードの番号は十進数で 97 である。
| 番号 | 書式 | 式1 | 式2 | 文字の番号 | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ||||
| 例 | "%d %d" |
2 * 2 | 3 + 7 | 4 | SP | 1 | 0 | ||||||
"%10s" |
"Sagami" | SP | SP | SP | SP | S | a | g | a | m | i | ||
"%d|%d" |
33 % 7 | 22 / 6 | 5 | | | 3 | ||||||||
"%d*%d" |
160 >> 4 | 17 & 18 | 1 | 0 | * | 1 | 6 | ||||||
"%x %c" |
'd' | 26 << 2 | 6 | 4 | SP | h | |||||||
"%d %d" |
13 ^ 24 | 400 / 50*2 | 2 | 1 | SP | 1 | 6 | ||||||
"%-3d-%3d" |
0x1C | 066 | 2 | 8 | SP | - | SP | 5 | 4 | ||||
"%*d" |
6 | 3 | (int)0.00355E4 | SP | SP | SP | SP | SP | 3 | 5 | ||||
"%.3f%%s" |
(double)(37/5) | "abc" | 7 | DOT | 0 | 0 | 0 | % | s | ||||
"%+d %d" |
8 & 40 | 3 | 5>7?3:2 | + | 1 | 1 | SP | 2 | ||||||
次の質問 (一部省略) が Q & A フォーラムに投稿された。質問に適切に答えなさい。
この授業で一番分かりにくかったことを書きなさい。(具体的に書いてあれば内容にかかわらず 3 点)
@@@@
この授業で一番勉強になったことを簡単に説明しなさい。(具体的に書いてあれば内容にかかわらず 3 点)
@@@@
前期試験 ・ 2018 年 7 月 26 日 4 時限実施 ・ ページ
テストの点数、出席日数、課題提出点から学生の成績を判定するプログラムを作成する。
学生の成績を判定するために使う下記の構造体 Score の定義を
完成させなさい。
typedef struct{ char name[20]; // 学生の氏名 int exam; // テストの点数 int attendance; // 出席日数 int practice; // 課題提出点 } Score;
荘司さんの成績: 良 豊田さんの成績: 優 高野さんの成績: 可 横窪さんの成績: 不可 再評価 荘司さんの成績: 良 豊田さんの成績: 優 高野さんの成績: 可 横窪さんの成績: 可
以下のプログラムが正しく動作するようにプログラム中の空欄を埋めなさい。ただし、学生の評価スコア (judge)
は、「テストの点数 (最大100)」×0.5、「出席日数 (最大10)」×2、「課題提出点 (最大30)」×1の和とし、0〜100までの値とし、評価はプログラム中のコメントの通りである。
void print_evaluation(Score scores[], int num_scores) // 評価スコアの計算 { double judge; int i; for (i = 0; i < num_scores; i++) { judge = scores[i].exam * 0.5 + scores[i].attendance * 2 + scores[i].practice; printf("%sさんの成績: ", scores[i].name ); if (judge < 60 ) // 60 点未満は「不可」 printf("不可\n"); else if (judge < 70 ) // 60 点以上 70 点未満は「可」 printf("可\n"); else if (judge < 80 ) // 70 点以上 80 点未満は「良」 printf("良\n"); else // 80 点以上は「優」 printf("優\n"); } } int main(void) { int i, num_scores = 4; Score scores[] = { { "荘司", 98, 8, 5 }, { "豊田", 100, 10, 30 }, { "高野", 62, 6, 20 }, { "横窪", 59, 5, 18 } }; print_evaluation(scores, num_scores); // 評価スコアを出力 for (i = 0; i < num_scores; i++) omake(&scores[i] ); // 救済措置の呼び出し (次頁参照) printf("\n再評価\n"); print_evaluation(scores, num_scores); // 再評価結果を出力 return 0; }
[次頁につづく]
前期試験 ・ 2018 年 7 月 26 日 4 時限実施 ・ ページ
| 授業 科目 |
計算機実習 I | 学生番号 | 学科 | 学年 | 組 | 番 | フリ ガナ |
評点 | ||||||||
| 氏名 | ||||||||||||||||
| 担当者 | DÜRST, Martin J.、莊司、 高野、豊田、横窪 |
|||||||||||||||
[前頁からつづく]
前頁の main 関数の中から呼び出されている omake 関数の実装を完成し、横窪さんを救済しなさい。omake
関数は出席回数が5回以上の場合、テストの点数を5点増やす。なお、その結果テストの点数が100点を超える場合、100点に制限する。
void omake(Score *score) // 参照渡し
{
if ( score->attendance >= 5)
{
score->exam += 5;
if ( score->exam > 100)
score->exam = 100;
}
}
プログラミング一般やプログラミング言語 C/C++ において使われる次の英語の用語の日本語訳と説明を書きなさい。日本語訳ではできるだけ片仮名を使わないようにしてください。
regression test
退行テスト/回帰テスト/再帰テスト; プログラムの部分的変更の影響を感知するテスト
indent
字下げ; プログラムの構造を分かりやすくする各行の先頭のスペースやタブ
modulo operator
剰余演算子; a % b で a を b で割っての余りを計算する演算子
derived type
派生型; 基本的な型から派生された型、例: 配列、ポインタ、構造体、関数
stack
スタック; 関数の呼び出しに伴う引数や局所的変数に使われるメモリ領域
independence
独立性; プログラムが複雑に絡み合うのではなく、各部品が出来るだけ独立していること
compound statement
複文; {} で囲まれる複数の文が一つの文とみなされるもの
standard input
標準入力; キーボードやファイルからのリダイレクトからの入力
loop invariant
繰り返しの普遍条件; 繰り返しにおいて、常に保たれる条件、動作を証明するために使われる
tail recursion
末尾再帰; 関数の最後に再帰すること、最適化される場合がある
前期試験 ・ 2018 年 7 月 26 日 4 時限実施 ・ ページ
ある助教が、5つの関数と、5つの printf 文で自己紹介しようとした。
ところが、その助教はC言語の知識がいまいちなので、たまに間違ったコードを書く。
そこで、それぞれの記述について、助教が意図したであろう挙動をするかを〇か×で判定し、
〇の場合は実際の出力とその説明を、×の場合は間違いの理由とその修正案を書きなさい。
char *get_name(void)
{
char moji[] = "Yoshiyuki Shoji";
return moji;
}
int rec(int i)
{
if (i == 1)
return 1;
else
return i * rec(i - 1);
}
int ext_weight(int w_and_h[2])
{
w_and_h[1] *= 2;
return w_and_h[0];
}
void modify_birthday(int birthday)
{
birthday = 1124;
}
int bit_calc(int num)
{
int m = num & 15;
m = m ^ 14;
m = m << 2;
return m;
}
// メイン関数部分
void main()
{
int age;
int w_and_h[2] = {58, 84};
int w, h;
int birthday = 0;
// (1)
printf("Hi, my name is %s.\n", get_name());
age = rec(4) + rec(3) + rec(2);
// (2)
printf("I'm %d years old.\n", age);
w = ext_weight(w_and_h);
h = w_and_h[1];
// (3)
printf("My weight: %d kg, my height: %d cm.\n",
w, h);
modify_birthday(birthday);
// (4)
printf("I was born on %d.\n", birthday);
// (5)
printf("I have %d bicycles.\n", bit_calc(65535));
return 0;
}
| 番号 | 〇か×か | 〇の場合、出力と説明 / ×の場合、理由と修正案 (○×: 各1点、記述: (5) は5点、 その他各3点) |
|---|---|---|
| (1) | × | 配列 (=ポインタ) だけを返している。関数内の文字列のメモリが 上書きされる可能性が高い。malloc などが必要。 |
| (2) | 〇 | I'm 32 years old. 再帰関数です。rec(i)はiの階乗を計算する関数なので、4!+3!+2!になります。 |
| (3) | 〇 | My weight is 58 kg, and my height is 168 cm. 副作用のある関数。配列・ポインタの引数では関数内で中身を書き換えられる。 |
| (4) | × | 値渡しで引数を渡した場合、関数に渡されるのはコピーなので、 変更しても元の変数には反映されません。 |
| (5) | 〇 | I have 4 bicycles. 次の通りのビット演算です。 1111111111111111 ← 655350000000000001111 ← 「&」演算で15(1111)と論理積。両方で1の桁だけで1が残る。0000000000000001 ← 「^」演算で14(1110)とXORをした。15と14のビット列で、異なるのは最下位1ビットだけ。 0000000000000100 ← 「<<」演算で2ビット分、左にずらした。4倍したのと同じ効果。 |
前期試験 ・ 2018 年 7 月 26 日 4 時限実施 ・ ページ
| 授業 科目 |
計算機実習 I | 学生番号 | 学科 | 学年 | 組 | 番 | フリ ガナ |
評点 | ||||||||
| 氏名 | ||||||||||||||||
| 担当者 | DÜRST, Martin J.、莊司、 高野、豊田、横窪 |
|||||||||||||||
経済学者が所得の分布を調べるためのプログラムの作成を依頼してきた。入力は n+1
個の整数である。最初の整数は所得のデータの数 n (最大 1000) で、その後に n
個の所得のデータ (正の整数、単位: 万円) が並ぶ。そのデータの最大値、平均値 (小数点以下2桁まで)、
そして平均以下のデータの数を出力例に合わせて出力するようにプログラムを書きなさい。
なお、プログムができるだけそのままでどの計算機でもコンパイル、実行できるよう、読みやすいように記述しなさい。
9 700 600 500 400 450 350 700 800 1800
Average: 700.00 万円 Maximum: 1800 万円 Number average or below: 7
#include <stdio.h>
int main(void)
{
int n, i;
int maximum = 0, lower_count = 0;
double sum = 0, average;
int incomes[1000];
scanf("%d", &n);
for (i = 0; i<n; i++) {
scanf("%d", &incomes[i]);
if (incomes[i] > maximum)
maximum = incomes[i];
sum += incomes[i];
}
average = sum / n;
for (i = 0; i<n; i++) {
if (incomes[i] <= average)
lower_count++;
}
printf("Average: %23.2f 万円\n", average);
printf("Maximum: %20d 万円\n", maximum);
printf("Number average or below: %4d\n", lower_count);
}
前期試験 ・ 2018 年 7 月 26 日 4 時限実施 ・ ページ
下記のプログラム断片は、Twitter を利用するユーザのフォロー数、フォロワー数を出力するプログラムである。
#include <stdio.h>
class TwitterUser {
public:
/* Constructor 1 の宣言 */
/* Constructor 2 の宣言 */
void setFollow(int fw);
int getFollow(void);
private:
int follow;
int follower;
};
/* Constructor 1 の定義 */
/* Constructor 2 の定義 */
void TwitterUser::setFollow(int fw)
{
follow = xfw;
}
int TwitterUser::getFollow(void)
{
return follow;
}
int main(void)
{
/* Taro の定義 */
/* Taro のフォロー数とフォロワー数の出力 */
}
引数なしでフォロー数とフォロワー数を0に設定するコンストラクタ (Constructor 1) と,2つの整数型の引数を与えて フォロー数,フォロワー数を設定するコンストラクタ (Constructor 2) の2つを定義しなさい。 定義に合わせて、クラス内のコンストラクタの宣言も書きなさい。
TwitterUser::TwitterUser(void)
{
follow = 0;
follower = 0;
}
TwitterUser(void);
TwitterUser::TwitterUser(int fw, int fer)
{
follow = fw;
follower = fer;
}
TwitterUser(int fw, int fer);
フォローワー数のセッタ (setFollower、左) およびゲッタ (getFollower、右) をそれぞれ定義しなさい。
void TwitterUser::setFollower(int fer)
{
follower = fer;
}
int TwitterUser::getFollower(void)
{
return follower;
}
Taro のフォロー数は300人,フォロワー数は450人である。コンストラクタを使って変数 Taro を定義し、 その変数を使って Taro のフォロー数とフォロワー数を出力するように下記のプログラム断片を完成しなさい。
TwitterUser Taro(300,450); printf("Taro:フォロー %d フォロワー %d\n", Taro.getFollow(), Taro.getFollower() );
main 関数内で Taro.follow や Taro.follower を使用するとエラーになる。
エラーになる原因を説明しなさい。
private であるメンバ変数を参照しようとしたから。
前期試験 ・ 2018 年 7 月 26 日 4 時限実施 ・ ページ
| 授業 科目 |
計算機実習 I | 学生番号 | 学科 | 学年 | 組 | 番 | フリ ガナ |
評点 | ||||||||
| 氏名 | ||||||||||||||||
| 担当者 | DÜRST, Martin J.、莊司、 高野、豊田、横窪 |
|||||||||||||||
以下のプログラムには 3 箇所の誤りがある.誤りの箇所の行番号を指摘し、実行例のような結果が得られるようにするための修正方法を示しなさい.ただし、9 行目で定義される変数
x のアドレスは 0x7ffee960a848 とする。
1 #include <stdlib.h>
2 void f (int* y)
3 {
4 printf("%p %p %d\n", &y, y, *y);
5 }
6
7 int main (void)
8 {
9 int x = 10;
10 int* ptr = x;
11
12 f(x);
13 *ptr &= 10;
14 f(0);
15 x = x *x + *ptr;
16 printf("%d\n", x);
17
18 return 0;
19 }
% ./a.exe
0x7ffee960a818 0x7ffee960a848 10
110
%
| 番号 | 修正箇所 | 修正方法 |
|---|---|---|
| 例 | 1行目 | stdlib.h を stdio.hに変更する. |
| (1) | 10行目 | int* ptr = x; を int* ptr = &x; に変更
// ポインタの初期化にアドレスが必要 |
| (2) | 12行目 | f(x); を f(&x);に変更
// f() の引数はポインタなので |
| (3) | 14行目 | f(0); を取り除く
// Segmentation Fault |
eq の定義 (5点)関数 eq は int 型のポインタ引数を 2 つとり,
それらのポインタが同じかを比較し、
その比較の結果を返す。
int eq(int* p1, int* p2)
{
return p1 == p2;
}
eqv の定義 (5点)関数 eqv は int 型のポインタ引数を 2
つとり,
それらのポインタが指している変数が保持している値が
同じかを比較し、その比較の結果を返す。
int eqv(int* p1, int* p2)
{
return *p1 == *p2;
}
前問で定義した関数 eq と eqv について、ある int 型のポインタ p1 と p2 に対して
printf("%d %d", eq(p1,p2), eqv(p1,p2));
を実行したときに、p1 と p2 にどんな値が代入されていようとも、「1 0」という出力が得られることは決してない。
その理由を書きなさい。
ポインタが指している先の領域が同じであるのに,そこに入っている値が異なることはないため。