青山学院大学

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

授業
科目
計算機実習 I 学生番号 学科 学年 フリ
ガナ
  評点
                   氏名    
担当者 DÜRST, Martin J.、松原、
斉藤、白川、高橋、盛川

式の評価 (16 点)

次のテーブルの式の値を計算しなさい。

番号 番号
2+3*5 17 2+3*5 17
23 % 6 5 8 - 10-4 -6
7 * 5, 3 * 8 24 16 / 5 3
2 << 7 256 4 > 3 + 2 >= 5 0
16 - 016 2 0xA * 0xC 120
16 / 5.0 3.2 15 & 27 11
'k' - 'f' 5 (int) 22.873E2 2287
15 | 27 31 66 >> 2 16
3 > 0? 15:6 15 3+8 * 7 59

英語の用語 (20 点)

プログラミング一般やプログラミング言語 C/C++ において使われる次の英語の用語の日本語訳と説明を書きなさい。日本語訳ではできるだけ片仮名を使わないようにしてください。

warning
警告; エラーメッセージと違ってコンパイルは成功が、残さないでプログラムを修正した方がよい

recursion
再帰; ある関数から同じ (名前と役割を果たす) 関数を呼ぶこと

declaration
選言; ものの存在、名前、型だけを知らせ (実態の中身は後で定義)

standard input
標準入力; キーボードやリダイレクトのファイルからの入力

local variable
局所変数; 関数や複文の中を有効範囲として持つ変数

assignment operators
代入演算子; 変数に代入する演算子、= 以外に +=, -=, /=, <<= などがある

regression test
退行テスト/回帰テスト/再帰テスト; プログラムの部分的変更の影響を感知するテスト

dereference operator
間接演算子; ポインタが指す値を返す演算子 (単項 *)

derived type
派生型; 基本的な型から派生された型、例: 配列、ポインタ、構造体、関数

identifier
識別子; 変数、引数、関数、型などに使う名前

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

素数の関数 (18 点)

基本のプログラム (10 点)

次のプログラムは0から99までの素数を表示する。正しく動作するようにプログラム中の空欄を埋めなさい。

#include <stdio.h>
#define MAX 100

int isPrime(int num)
{
    int i;
    for (i=2; i<num; i++) {
        if (num%i == 0)
            return 0;
    }
    return 1;
}

void printIntArray(int *data, int num)
{
    int i;
    for (i=0; i<num; i++) {
        printf("%3d\n", data[i]);
    }
}

int main()
{
    int i, count=0, primes[MAX];
    for (i=2; i<MAX; i++) {
        if (isPrime(i)) {
            primes[count] = i;
            count++;
        }
    }
    printIntArray(primes, count);
    return 0;
}

関数の追加 (8 点)

上記のプログラムの isPrime 関数を用いて、与えられた数がメルセンネ素数かどうかを判定する isMersenne 関数を追加したい。メルセンヌ素数とは、素数のうち2n-1で表せる数である (n は自然数)。例えば7は23-1なのでメルセンヌ素数である。main 関数中の isPrime 関数の呼び出しを isMersenne 関数の呼び出しに変更するだけで正しく動作するように、下記の空欄を埋めなさい。

#include <math.h>

int isMersenne(int num)
{
    int i, m=0;
    if (isPrime(num)) {
        for (i=2; m<num; i++) {
            m = pow(2, i)-1;
            if (num == m)
                return 1;
        }
    }
    return 0;
}

青山学院大学

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

授業
科目
計算機実習 I 学生番号 学科 学年 フリ
ガナ
  評点
                   氏名    
担当者 DÜRST, Martin J.、松原、
斉藤、白川、高橋、盛川

ポインタ (合計 16 点)

ポインタの操作 (10 点)

次のプログラム断片の実行後の、下記の表の (a) から (j) までの式の値を記入しなさい。

int ma=15, mb=25;
int mx=0, my=0;
int mc[3] = { 10, 20, 35 };
int *pa, *pb, *pc, *px, *py;

pa = &ma;  pb = &mb;  pc = mc;
px = &mx;  py = &my;

pc++;
mx = *pa+*mc+1;
my = *(pc+1)+*pb;
*pa = ma+*px;
*pb = *(mc+1) + *(mc+2);
(a) ma    41    (b) mb    55    (c) *pa    41    (d) *pb    55    (e) *pc    20   
(f) mx    26 (g) my    60 (h) *px    26 (i) *py    60 (j) *mc    10

配列の引数 (6 点)

下記左のプログラムの実行結果を下記右に書き込みなさい(実行結果の空欄部分を埋めなさい)

#include <stdio.h>

int func1(int *a, int n)
{
    int i, temp=0;

    for (i=0; i<n; i++)
        temp+=*(a+i);

    return(temp);
}

void func2(int *b, int m)
{
    int j, k=0;

    k = func1(b, m);
    for (j=0; j<k; j++)
        printf("*");
    printf("\n");
}

int main(void)
{
    int x[3] = { 2, 1, 1 };
    int y[4] = { 1, 2, 3, 1 };
    int z[5] = { 1, 0, 2, 0, 1};

    printf("1: "); func2(x,3);
    printf("2: "); func2(y,4);
    printf("3: "); func2(z,5);

    return 0;
}

実行結果

1:  ****           
2:  *******        
3:  ****           

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

C++ (合計 20 点)

以下のプログラムは運動部の選手情報を入力し、最も強い選手を出力する。 クラス Player は選手を、クラス Club は運動部を表す。

穴埋め (6 点)

下記のプログラム (2 段組、ファイル club.hclubMain.cppclub.cpp) で空欄を埋めなさい。

// club.h
#include <string.h>
#define PLAYER_MAX 80

class Player {
    int identifier, strength;
  public:
    Player() {};
    Player(int id, int st);
    int getStrength();
    void print();
};

class Club {
    char name[100];
    int numberOfPlayers;
    Player players[PLAYER_MAX];
  public:
    Club(const char* s);
    void addPlayer(Player p);
    Player bestPlayer();
    void inputPlayers();
};

// clubMain.cpp
#include "club.h"

int main(void)
{
    Club a("Aoyama Sumo Team");
    a.inputPlayers();
    a.bestPlayer().print()    ;
    return 0;
}

// club.cpp
#include "club.h"
#include <stdio.h>

Player::Player(int id, int st)
{
    identifier = id;
    strength = st;
}
int Player::getStrength()
{
    return strength;
}

void Player::print()
{
    printf("identifier: %d, strength: %d\n",
            identifier, strength);
}

Club::Club(const char* s)
{
    numberOfPlayers = 0;
    strcpy(name, s);
}

void Club::addPlayer(Player p)
{
    if (numberOfPlayers < PLAYER_MAX) {
        players[numberOfPlayers++] = p;
    }
}

Player Club::bestPlayer()    
{
    int i, bi = 0;
    for (i = 0; i < numberOfPlayers; i++) {
        if (players[i].getStrength() >
                players[bi].getStrength()) {
            bi = i;
        }
    }
    return players[bi];
}

void Club::inputPlayers()
{
    int id, st;
    while(scanf("%d, %d", &id, &st) == 2) {
        Player p(id, st)    ;
        addPlayer(p);
    }
}

コンストラクタの数 (2 点)

空欄にコンストラクタの数を入れなさい。

クラス Player のコンストラクタの数: 2   個; クラス Club のコンストラクタの数: 1   個。

[次ページに続く]

青山学院大学

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

授業
科目
計算機実習 I 学生番号 学科 学年 フリ
ガナ
  評点
                   氏名    
担当者 DÜRST, Martin J.、松原、
斉藤、白川、高橋、盛川

[前ページから続く]

メンバ関数 (4 点)

前ページのプログラムに、メンバ関数 setStrength を追加で定義する。このメンバ関数は、整数 n を渡されると、n の値が 0 以上ならば、メンバ変数 strengthn を設定し、そうでなければ 0 を設定する。setStrength の定義を書きなさい。ただし、クラスの宣言は適切に変更されていると考えてよい。

void Player::setStrength(int n)
{
    strength = n>0 ? n : 0;
}

カプセル化 (4 点)

前ページのプログラムではデータのカプセル化が実現されている。そのカプセル化がどのように実現されているかを、プログラム中からメンバ変数を一つ選んだ上で、適切な専門用語を用いて、具体的かつ簡潔に説明しなさい。

Player クラスの strength は private なメンバ変数なので、値を取得するには public なメンバ関数 getStrength を用いる。getStrength のようなはオブジェクト指向プログラミングにおいて一般にゲッターと呼ばれる。保護したいデータへのアクセスのために定義する。

C 言語との比較 (4 点)

C++ のクラスは、C 言語の構造体と関数をまとめたものと考えることができるが、カプセル化など、構造体と関数の組合せ以上の利点がある。 上のプログラムから、カプセル化以外の利点が現れている部分を一つ挙げ、C 言語で実現した場合と比較したときの利点をできる限り具体的に述べなさい。

「C++ では、clubMain.cppa.bestPlayer() のように、操作対象のオブジェクト a をレシーバとして、メンバー関数 bestPlayer を呼び出すことができる。一方 C 言語で同様なことをする場合、構造体 Club を引数として渡す関数 bestPlayer を定義する必要がある。C++ の書き方の方が、現実の対象との対応が理解しやすく、分かりやすい。」 など。(レシーバー以外にもコンストラクタに関する点など)

プログラム実行中の停止の原因 (8 点)

プログラムが実行中にエラーで停止する原因を四つ列挙し、説明しなさい。

Stack overflow:
関数の深い (再帰的の) 呼び出しで、スタックのために用意されているメモリが足りなくなっる

Memory leak:
解放できる動的メモリを解放しないで、必要以上にメモリが用意され、足りなくなる。

Segmentation fault:
プログラムに与えられたメモリ領域を超え、「隣の庭」に侵入しようとしている。

Double free:
解放したメモリをもう一回解放しようとする。

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

プログラムの問題の指摘 (30点)

下記のプログラムは学生のデータ (姓名それぞれの頭文字、学籍番号、入学年、点数の平均) を読み込み、基準に照らし合わせて、合格・危険・不合格の結果を出力する。 このプログラムには様々なレベルや種類の間違いや不備がある。授業で習った点や指摘されそうな点も含め、コメントの形で問題と修正方法を簡単に記述しなさい。 問題の数は合計で 23個。一部の問題で 2点が得られる。できるだけ沢山の問題を見つけなさい。同じ問題が二回以上ある場合、最初の一回しか指摘しないこと。

#include<stdio.h>  // 例: include 後、スペースを追加
                   //// ① は 1点、② は 2点
struct Student {   
    char   firstname_initial;  // ② int, short, char, char, double などの
    int    student_number;     //    順番でメモリの節約
    char   myouji_kashiramoji;  // ① メンバ名は一つの言語に統一
       short  entry_year;  // ① 字下げが多過ぎ
    double grade_average;   
};   
typedef struct Student Student;  // ② typedef は一発で: typedef struct {} Student;
   
int main (void) {  // ① { の前に改行
    FILE *f;  // ① 変数名は短すぎる、例えば file とか input_file がいい
    int flag = 1;   
    Student student:  // ① コロン ⇒ セミコロン

    f = fopen("students.raw", "r");  // ① バイナリ入力なので "r" ⇒ "rb"
                                     // ② ファイルが開けない場合の対応が必要
    for (i=0; flag; i++) {  // ② while (0) に変換した方が自然
        if (!fread(&student, 1, sizeof(Student), f)) {  // ① 1 と sizeof の順番が逆
            flag = 0;  // ② break; への変換で flag が不要になる
        if (flag && student->entry_year <= 2011) {  // ① -> ⇒ .
            if (student->grade_average => 60)  // ① => ⇒ >=
                printf("Passed:    %d (%c%c, average: %d)\n", // ① 最後の %d ⇒ %g
                       student->number, student->firstname_initial  // ① 行末にコンマ不足
                       student->myouji_kashiramoji, student->grade_average);   
            elsif (student->grade_average => 50)  // ① elsif ⇒ else if
                printf("In danger: %d (%c%c, average: %d)",  // ① 改行出力欠落
                       student->number, student->firstname_inisial, // ① inisial: s ⇒ t
                       student->myouji_kashiramoji, student->grade_average);   
        else  // ① 字下げが足りない、4文字追加
                printf("Failed:    %d (%c%c, average: %d)\n",  // ② 三つの printf
                       student->number, student->firstname_initial,  // が類似、関数化
                       student->myouji_kashiramoji, student->grade_average);   
        }   
    }    
      // ② 使用後ファイルを閉じる: fclose(f); を追加
    return(0);  // ① return は文なので、括弧は不要
}   

青山学院大学

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

授業
科目
計算機実習 I 学生番号 学科 学年 フリ
ガナ
  評点
                   氏名    
担当者 DÜRST, Martin J.、松原、
斉藤、白川、高橋、盛川

プログラムの作成 (合計 20 点)

小学校の先生から九九が練習できる穴埋めプリントを作るように頼まれた。下記の出力結果のプログラムを作成しなさい。 印刷されるか、穴埋めのために印刷されないかを、変更しやすいように一ヶ所で判断しなさい。 プログラムはできるだけそのままコンパイル、実行できるようにした方がいいが、一部分からない部分があっても残りをできるだけ書きなさい。

出力結果:

       2       4       6       8
   2       6      10      14      18
       6      12      18      24
   4      12      20      28      36
      10      20      30      40
   6      18      30      42      54
      14      28      42      56
   8      24      40      56      72
      18      36      54      72

作成用のプログラム:

#include <stdio.h>

int main (void)
{
    int i, j;

    for (i=1; i<10; i++) {
        for (j=1; j<10; j++) {
            if ((i+j)%2) {
                printf("%4d", i*j);
            }
            else {
                printf("    ");
            }
        }
        putchar('\n');
    }

    return 0;
}

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

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

@@@@

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

@@@@