青山学院大学

前期試験 ・ 2018 年 7 月 26 日 4 時限実施 ・ ページ

授業
科目
計算機実習 I 学生番号 学科 学年 フリ
ガナ
  評点
                   氏名    
担当者 DÜRST, Martin J.、莊司、
高野、豊田、横窪

式の評価と出力の書式 (23 点)

下記の表の「書式」、「式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 フォーラム (14 点)

次の質問 (一部省略) が Q & A フォーラムに投稿された。質問に適切に答えなさい。

手元で cannot open output file a.exe: Device or resource busy になった。助けてください。
a.exe はまだ実行中なので、コンパイラが上書きできません。a.exe を終了しないと。
warning: no newline at end of file がどうしてもなくならない。
ファイルの最後に改行が必要。「空白の表示」で確認するといい。
Cygwin と Program Checking System での挙動が違う。
まずは、初期化がきちんとできているかよく確認しましょう。
Program busy or waiting for too long! といわれたが、無限ループもないし、大した計算もやってない。
プログラムが更なる入力を待っているか、EOFを感知できてない可能性があります。
too few arguments to function 'fwrite' にはどうやって対応すればよいでしょうか。
fwrite は四つの引数をとっています。確認してください。
なぜか undefined reference to `_prinf' がでましたが、理由が分かりません。
「prinf」という関数を使おうとしていますが、printf のではないでしょうか。
`gets' function is dangerous と言われているが、なぜでしょうか。
用意されたバッファが長い入力の場合オーバされ、脆弱性などになる。fgets を使うべき。

授業へのコメント (合計 6 点)

この授業で一番分かりにくかったことを書きなさい。(具体的に書いてあれば内容にかかわらず 3 点)

@@@@

この授業で一番勉強になったことを簡単に説明しなさい。(具体的に書いてあれば内容にかかわらず 3 点)

@@@@

前期試験 ・ 2018 年 7 月 26 日 4 時限実施 ・ ページ

構造体 (合計 22 点)

テストの点数、出席日数、課題提出点から学生の成績を判定するプログラムを作成する。

構造体の型宣言 (2点)

学生の成績を判定するために使う下記の構造体 Score の定義を
完成させなさい。

typedef struct{
    char name[20];    // 学生の氏名
    int  exam;        // テストの点数
    int  attendance;  // 出席日数
    int  practice;    // 課題提出点
} Score;
  

全体の出力結果

荘司さんの成績: 良
豊田さんの成績: 優
高野さんの成績: 可
横窪さんの成績: 不可

再評価
荘司さんの成績: 良
豊田さんの成績: 優
高野さんの成績: 可
横窪さんの成績: 可

構造体のデータ参照 (14点)

以下のプログラムが正しく動作するようにプログラム中の空欄を埋めなさい。ただし、学生の評価スコア (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.、莊司、
高野、豊田、横窪

[前頁からつづく]

参照渡し (5点)

前頁の 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; 
    }
}

英語の用語 / English Terms (20 点)

プログラミング一般やプログラミング言語 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 時限実施 ・ ページ

関数の問題点 (合計 22 点)

ある助教が、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 ← 65535
0000000000001111 ← 「&」演算で15(1111)と論理積。両方で1の桁だけで1が残る。
0000000000000001 ← 「^」演算で14(1110)とXORをした。15と14のビット列で、
異なるのは最下位1ビットだけ。
0000000000000100 ← 「<<」演算で2ビット分、左にずらした。4倍したのと同じ効果。

青山学院大学

前期試験 ・ 2018 年 7 月 26 日 4 時限実施 ・ ページ

授業
科目
計算機実習 I 学生番号 学科 学年 フリ
ガナ
  評点
                   氏名    
担当者 DÜRST, Martin J.、莊司、
高野、豊田、横窪

プログラムの自由記述 (25点)

経済学者が所得の分布を調べるためのプログラムの作成を依頼してきた。入力は 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 時限実施 ・ ページ

C++ (合計 20 点)

下記のプログラム断片は、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 のフォロー数とフォロワー数の出力 */
}

コンストラクタの定義 (8 点)

引数なしでフォロー数とフォロワー数を0に設定するコンストラクタ (Constructor 1) と,2つの整数型の引数を与えて フォロー数,フォロワー数を設定するコンストラクタ (Constructor 2) の2つを定義しなさい。 定義に合わせて、クラス内のコンストラクタの宣言も書きなさい。

Constructor 1 の定義

TwitterUser::TwitterUser(void)
{
    follow   = 0;
    follower = 0;
}

Constructor 1 の宣言

TwitterUser(void);

Constructor 2 の定義

TwitterUser::TwitterUser(int fw, int fer)
{
    follow   = fw;
    follower = fer;
}

Constructor 2 の宣言

TwitterUser(int fw, int fer);

メンバ関数の定義 (6 点)

フォローワー数のセッタ (setFollower、左) およびゲッタ (getFollower、右) をそれぞれ定義しなさい。

void TwitterUser::setFollower(int fer)
{
    follower = fer;
}
int TwitterUser::getFollower(void)
{
    return follower;
}

プログラムの修正 (4 点)

Taro のフォロー数は300人,フォロワー数は450人である。コンストラクタを使って変数 Taro を定義し、 その変数を使って Taro のフォロー数とフォロワー数を出力するように下記のプログラム断片を完成しなさい。

TwitterUser Taro(300,450);
printf("Taro:フォロー %d フォロワー %d\n", Taro.getFollow(),  Taro.getFollower()  );

エラーの原因 (2 点)

main 関数内で Taro.followTaro.follower を使用するとエラーになる。 エラーになる原因を説明しなさい。
private であるメンバ変数を参照しようとしたから。

青山学院大学

前期試験 ・ 2018 年 7 月 26 日 4 時限実施 ・ ページ

授業
科目
計算機実習 I 学生番号 学科 学年 フリ
ガナ
  評点
                   氏名    
担当者 DÜRST, Martin J.、莊司、
高野、豊田、横窪

ポインタ / Pointers (合計20点)

間違え探し (6点)

以下のプログラムには 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.hstdio.hに変更する.
(1) 10行目 int* ptr = x;int* ptr = &x; に変更 // ポインタの初期化にアドレスが必要
(2) 12行目 f(x);f(&x);に変更       // f() の引数はポインタなので
(3) 14行目 f(0); を取り除く       // Segmentation Fault

関数 eq の定義 (5点)

関数 eqint 型のポインタ引数を 2 つとり,
それらのポインタが同じかを比較し、
その比較の結果を返す。

int eq(int* p1, int* p2)
{
    return p1 == p2;
}

関数 eqv の定義 (5点)

関数 eqvint 型のポインタ引数を 2 つとり,
それらのポインタが指している変数が保持している値が
同じかを比較し、その比較の結果を返す。

int eqv(int* p1, int* p2)
{
    return *p1 == *p2;
}

ポインタとアドレス (4点)

前問で定義した関数 eq と eqv について、ある int 型のポインタ p1 と p2 に対して

printf("%d %d", eq(p1,p2), eqv(p1,p2));

を実行したときに、p1 と p2 にどんな値が代入されていようとも、「1 0」という出力が得られることは決してない。
その理由を書きなさい。

ポインタが指している先の領域が同じであるのに,そこに入っている値が異なることはないため。