青山学院大学

前期試験 ・ 2010 年 8 月 5 日 4 時限実施 ・ ページ

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

英語の用語 (18 点)

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

indirection
間接; ポインタでデータの二つの部分や見方をつなぎ、新しい応用を生み出せる

return value
戻り値; 関数の結果として return で戻される値

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

tail recursion
末尾再帰; 関数の最後に再帰すること

independence
独立性; プログラムが複雑に絡み合うのではなく、各部品が出来るだけ独立していること

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

identifier
識別子; 変数、関数、型などの名前、選び方が大切。

indent
字下げ; プログラムの構造を分かりやすくする各行の先頭のスペースやタブ

side effect
副作用; 関数の戻り値以外の効果(グローバル変数の変化、画面への印刷など)

演算子の説明 (12 点)

次の演算子の効果を説明しなさい。

++ インクリメント (前置又は後置): 変数に1 を足す。

*= 左の変数に右の式の結果を掛けて代入する

-> 左の構造体のポインタから右のメンバへアクセスする

? : 三項演算子、? の前の条件の結果により、: の左か右を返す

, コンマ演算子、二つの式を結び、右の指揮の結果だけを返す

~ ビット毎「ではない」

前期試験 ・ 2010 年 8 月 5 日 4 時限実施 ・ ページ

class の設計 (10 点)

C++ で長さを保存できるクラスを作る。設定、問い合わせはメートルでもフィートでもできるようなメンバ関数を用意する。クラスの定義は以下のようになる。

class Length {
    // メンバー変数
public:
    Length();
    void set_meters(double m);
    void set_feet(double y);
    double get_meters();
    double get_feet();
};

メンバ変数の選択肢としては次のいくつかが考えられる。

  1. metersfeet を両方用意し、set_ 関数で両方設定する。
  2. metersfeet のどちらかを用意し、set_ 関数と get_ 関数のそれぞれどちらか一つで変換を行う。
  3. metersfeet と印を用意し、set_ 関数でセットされたものを印で記録、get_ 関数で印に応じて変換。

上記の選択肢から最善のものを選んで、明記の上にその理由を、オブジェクト指向について学んだ知識を使いながら細かく説明しなさい。

2. を選択。オブジェクト指向では基本的にはオブジェクトの中身を外から見えないようにし、
中身 (class の実装) をいつでも変えられるようにすることが大事です。
その観点からどちらを選んでもいいですが、実際の実相を考えるとメンバ変数が一つだけで、
例えば meters だけの方が一番すっきりする。なぜなら、既にできているメンバ関数を
変更しなくても例えば set_inches と get_inches のように新たなメンバ関数を簡単に
追加できる。オブジェクト指向の大きいの関心ではありません 2 番ではメモリも少なくなる。

定石 (合計 16 点)

プログラム言語 C には「定石」 (一旦複雑に見えるが筋が通った決まり文句) はいくつかある。知っている「定石」を一つ選び、プログラムの断片で書いて、説明しなさい。

プログラムの断片 (8 点)

    int *dynamic_array;
    int items = 200;
    if (!(dynamic_array=(int*)malloc(sizeof(int)*items)))
        fprintf(stderr, "Not enough memory!\n"), exit(1);
  

説明 (8 点)

最初の二行は準備のための定義。その後の二行で必要なメモリを計算 (sizeof(int)*items)、メモリの確保 (malloc)、void* の malloc の戻り値の型変換 ((int*))、 メモリのアドレスをとっておく dynamic_array への代入、メモリが無いときの場合 NULL が返ってくるか のチェック (if (! )、標準エラー出力への「メモリが無い」のメッセージの出力 (fprintf(...))、 そして最後にはプログラムの非常脱出 (exit(1)、0 以外は「問題発生」という意味) を二行にまとまって 行っている。
[ファイルを (エラーメッセージを含め)開くとかポインタを使った文字列の処理なども可能]

青山学院大学

前期試験 ・ 2010 年 8 月 5 日 4 時限実施 ・ ページ

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

ポインタと参照渡し (合計 15 点)

実行結果の穴埋め (10 点)

以下は、あるプログラムと実行結果である。実行結果の空欄を埋めなさい。

プログラム (二段組み):

#include <stdio.h>

void f1 (int a, int b) {
    a = b;
    b = a;
}

void f2 (int *pa, int *pb) {
    *pa = *pb;
    *pb = *pa;
}

void f3 (int c[]) {
	int d;
	d = c[0];
	c[0] = c[1];
	c[1] = d;
}
int main()
{
    int x, y;
    int z[2];

    x = 1; y = 2;
    f1(x, y);
    printf("x = %d, y = %d\n", x, y);
    f2(&x, &y);
    printf("x = %d, y = %d\n", x, y);

    z[0] = 4; z[1] = 7;
    f3(z);
    printf("z[0] = %d, z[1] = %d\n", z[0], z[1]);

    z[1] = z[0] + z[1];
    f3(z);  f2(&z[0], &z[1]);
    printf("z[0] = %d, z[1] = %d\n", z[0], z[1]);

    z[1] = z[0] + z[1];
    f1(z[0], z[1]);  f2(&z[0],&z[1]);  f3(z);
    printf("z[0] = %d, z[1] = %d\n", z[0], z[1]);
}

実行結果

x = , y = 
x = , y = 
z[0] = , z[1] = 
z[0] = , z[1] = 
z[0] = 14, z[1] = 14

配列引数 (5 点)

上記関数 f3 は配列を引数として受け取るが、これは参照渡しと値渡しのどちらに分類するのが適切か、理由も含めて述べなさい。

C 言語の場合、配列の先頭のアドレスのみを関数に渡して、それを利用して間接的に配列の各要素の値を参照している。従って、参照渡しに分類するのが適切である。

前期試験 ・ 2010 年 8 月 5 日 4 時限実施 ・ ページ

ファイル出力 (合計 17 点)

プログラムの穴埋め (9 点)

EOFが検出されるまで標準入力から半角文字を読み込み、アルファベットの大文字、小文字の変換を行ってファイル "out.txt" に出力する次のプログラムを考える。なお、isupper/islower はそれぞれある文字が大文字/小文字の時に真を返す関数で、toupper/tolower はそれぞれある文字を大文字/小文字に変換する関数である。空欄を埋めなさい。

#include <stdio.h>
#include <ctype.h>

int main (void)
{
    int c;
    FILE   *fp   ;

    if ((fp =   fopen  ("out.txt", "w"  )) == NULL) {
        printf("ファイルをオープンできません\n");
        return 1;
    }

    while ((c=  getchar()  ) != EOF){
        if (isupper(c)) {
            c=tolower(c);
        }
        if (islower(c)) {
            c=toupper(c);
        }

         [f]putc (  c  ,   fp  );
    }

    fclose(  fp  );

    return 0;
}

出力の推測とプログラムの修正 (8 点)

上記プログラムの実行時に標準入力へ次のような文字列を入力した時に "out.txt" に出力される文字列を選んで回答欄に◯をつけなさい。また、◯のつかなかった文字列を出力するには上記プログラムのどこをどのように訂正すれば良いかを、該当する文字列の訂正箇所欄に書きなさい。

標準入力:

Aoyama Gakuin's history starts in 1874.[EOF]
ファイル出力文字列 回答欄 訂正箇所欄
aoyama gakuin's history starts in 1874.   if (islower(c)) { ... } を消去
aOYAMA gAKUIN'S HISTORY STARTS IN 1874.   if (islower(c)) {else if (islower(c)) { に変更
AOYAMA GAKUIN'S HISTORY STARTS IN 1874.
Aoyama Gakuin's history starts in 1874.   if (isupper(c)) { ... } を消去

青山学院大学

前期試験 ・ 2010 年 8 月 5 日 4 時限実施 ・ ページ

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

関数の使用と作成 (合計 18 点)

途中まで書かれた次のプログラムを読み,以下の問に答えよ.

#include <stdio.h>
#include <math.h>

#define DIM 3 // ベクトルの次元数

double innerProduct (double*, double*);
double vectorLength (double*);

int main (void)
{
    double x[DIM] = {3.0, 0.0, 4.0}, y[DIM] = {3.0, 1.0, -2.4}; // 2つのベクトル x と y

    printf("x と y の内積: %f\n", innerProduct(x,y)); // -0.600000
    printf("x の大きさ: %f\n", vectorLength(x));   //  5.000000

    return 0;
}

内積の関数の完成 (10 点)

上記のプログラムの続きに相当する、2つのベクトルの内積を計算する関数 innerProduct を空白を埋めて完成させなさい。

 double  innerProduct (  double *a, double *b  ) {
    int i;
    double result = 0.0;
    for   (i = 0; i < DIM; i++)   {
          result += a[i] * b[i];  
    }
    return result;
}

長さの関数の作成 (8 点)

上記のプログラムの続きに相当する、ベクトルの大きさを計算する関数 vectorLength を作成しなさい。ただし、上で完成させた関数 innerProduct を必ず用いること。また平方根の計算には関数 sqrt を使って良い。

 double  vectorLength  (double *a) { 
    return   sqrt(innerProduct(a, a));  
}

前期試験 ・ 2010 年 8 月 5 日 4 時限実施 ・ ページ

再帰と文字列ポインタ (合計 20 点)

プログラムの穴埋め (10 点)

下記は、再帰を利用して、与えられた文字列に含まれる 'A' の文字数を求める関数 countAのプログラム及び実行結果である。プログラムの空欄を正しく埋めなさい。

プログラム:

#include <stdio.h>

int countA(char* str){
    if (*str   == '\0')
        return 0    ;
    else if (*str == 'A'  )
        return countA(str+1)+1;
    else
        return countA(str+1  );
}

int main (void)
{
    char string[] = "ABcaA";
    printf("The number of 'A's is %d.\n", countA(string));
    return 0;
}

実行結果: The number of 'A's is 2.

キーボードからの入力 (6 点)

上記プログラムの char string[] = "ABcaA"; の部分を、キーボードから fgets を用いて文字列を読み込むように書き換えなさい。読み込む最大文字数は、終端文字を含めて 50 文字とする。

char string[50];
fgets(string, 50, stdin);

文字列入力関数の選択 (4 点)

文字列入力の関数は他にもある。なぜ fgets を使うべきか説明しなさい。

関数 gets とか scanf の %s もあるが、入力する文字列が用意されているメモリを超えるとプログラムが segmentation fault を起こしたり、ウイルスの侵入にもつながりうる。

文字コードの選択 (8 点)

日本では Shift_JIS と UTF-8 の文字コードがよく使用されている。プログラムで使うときのそれぞれの利点を二つずつ列挙しなさい。

Shift_JIS の利点:
漢字などは一文字二バイトしか必要無い (UTF-8 は三バイト)
昔からあるデータがそのまま使える
UTF-8 の利点:
使える文字 (珍しい漢字、ハングル、アラビア語など) が多い
プログラムは世界中で使える (Shift_JIS は日本に限定)

青山学院大学

前期試験 ・ 2010 年 8 月 5 日 4 時限実施 ・ ページ

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

printf の形式の指定 (14 点)

下記のプログラムの断片で、下記の表のそれぞれの出力になるように書式指定 (%指定に限定、できるだけ短く) と変数名 (一文字に限定) を記入しなさい。なお、数えやすさのため、出力では半角の空白を # で表しいる。

プログラムの断片:

    char c = 'z';  // 十六進数で 7A
    int i = 27;
    double d = 52.731;
    char s[] = "ABCDEF";

    printf("=-=書式指定=-=\n", 変数名);
番号出力書式指定変数名番号出力書式指定変数名
=-=27=-= %d i =-=27=-= %d i
=-=###27=-= %5d i =-=ABCDEF=-= %s s
=-=27%=-= %d%% i =-=z###=-= %-4c c
=-=52.731000=-= %f d =-=5.273100E+01=-= %E d
=-=52.7=-= %.1f d =-=+27=-= %+d i
=-=##52.73100=-= %10.7f d =-=7a=-= %x c
=-=33=-= %o i =-=ABCD=-= %.4s s
=-=000027=-= %06i i =-=ABCDE#####=-= %-10.5s s

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

授業でよかった点、そして改善すべき点などについて教えてください。

よかった点 (何か書いたら 3 点):

[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@]
[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@]

改善すべき点 (何か書いたら 3 点):

[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@]
[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@]