青山学院大学

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

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

英語の用語 (18 点)

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

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

compound statement
複文; {} 内で囲まれる複数の文が一つの文とみなされる

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

actual parameter
実引数; 関数の呼出し時に実際に関数に渡される値

heap
ヒープ; メインメモリの中、同的メモリの確保に使わらる領域

loop invariant
繰り返しの普遍条件; 繰り返しにおいて、常に保たれる条件、動作を証明するために使われる

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

syntax
構文; 文や式などの書き方 (例: if (条件) 文)

standard output
標準出力; 画面やリダイレクトのファイルへの出力

printf 書式指定文字列内の文字の意味 (16 点)

下記の printf 等で使われる書式指定文字列内の文字の意味を説明しなさい。

例: d: 符号付き整数を 10進法に変換

s: 文字列を (ナル文字が来るまでに) 出力

x: 符号無し整数を 16進法に変換、a-f を使用

f: 浮動小数点数の表示 (E, 指数無し)

-: 左揃えを指定するフラグ

c: 整数を一文字 (バイト) として出力

+: 正でも符号を必ず出力するフラグ

p: ポインタの出力 (16 進法)

*: フィールド幅や精度をその場ではなく、変数を使って指定できる

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

プログラムの分かりやすい書き方 (24 点)

C のプログラムの中には、正しく動作しても読みやすさなどの面でまだまだ改善できるものも多い。 下記のそれぞれのプログラム断片について、一番改善につながる変更を簡単に説明しなさい。(「...」 はプログラム断片の省略場所を示します。)

番号プログラム断片改善の提案とその理由
   for (i=0; i<50; i++)
  {  if (i%5
    ==0) printf
     ("\n"); }
プログラムの構造が一瞬で把握できるため、字下げ (インデント) と改行を徹底的に修正
    i = 0;
    while (i<20) {
        printf(...);
        i = i+1;
    }
while 文の直前にループ変数の初期化、その最後にループ変数の変更があるので、while 文を for 文に変更
    int a[10];
    a[0] = 27; a[1] = 33; a[2] = 67;
    a[3] = 0; a[4] = 77; a[5] = 73; ...
コードが短くなって、値が簡単に確認できるため、配列の初期化を使用
    for (...) {
        goukei += a;
        sqr_tot += a*a;
        CbT += a*a*a;
    }
変数名を分かりやすくし、書式と自然言語を統一 (できれば英語を使用)
    if (a[12]==0 || a[12]==4 || a[12]==15)
        b = 27;
    else if (a[12]==13 || a[12]==3)
        d = 17;
    ...
    else
        e = 12;
比較する値 (a[12]) が毎回同じで、整数値なので、if 文を switch 文に変更
    char array[15][100];
    for (i=0; i<15; i++)
        gets(array[i]);
関数 gets はメモリの不正な使用につながる可能性が高いので、安全な fgets に置き換えるべき
    for ( ; i+k > 20; ) {
        i = k-2;
        k = i+1;
    }
for 文の機能が全く活かされてないので、 while 文に変更
    if (m==1)
        return "January";
    else if (m==2)
        return "February";
    else if (m==3)
        return "March";
    ...
if 文の中のデータをデータ構造 (文字列の配列) にし、範囲チェックの上で配列からデータを取り出す。
    int string_length (char *string) {
        int length = 0;
        while (*string++)
            length++;
        return length;
    }
この関数は文字列の長さを計算しますが、もうすでに標準ライブラリに strlen 関数がありますので、そちらを使用すべき (#include <string.h> が必要)

青山学院大学

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

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

構造体とポインタ (合計 16 点)

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

下記のプログラムは二つの点の x座標と y座標を読み込み、その二点の中点を求めるプログラムである。空欄を埋めなさい。

#include <stdio.h>
 typedef  struct {
    double x;
    double y;
}    Point  ;

void inputPoint (Point *p) {
    scanf("%lf", &p->x);  scanf("%lf", &p->y);
}

Point middlePoint (Point *p) {
    Point m;
    m.x = (p->x + (p+1)->x) / 2;
    m.y = (p->y + (p+1)->y) / 2;
    return    m   ;
}

int main (void)
{
    Point p[2];
    Point m;
    int i;
    for (i=0; i<2; i++){
        inputPoint(  p+i  );
    }
    m = middlePoint(   p   );
    printf("Middle point x:%.2lf y:%.2lf\n", m.x, m.y);
    return 0;
}

inputPoint の引数の型の変更 (2 点)

上記の関数 inputPoint の引数の型を (Point p) に変更したとする。 下記の空欄を埋めて、コンパイルエラーが生じないように修正した inputPoint 関数を作成しなさい。

void inputPoint (Point p) {
       scanf("%lf", &p.x);  scanf("%lf", &p.y);   
}

変更の影響 (4 点)

上記修正に加え、main 関数での呼び出し部分も修正することで、 引数の型を変更しても、コンパイルエラーの無い実行可能なプログラムが作成できます。 しかしながら、そのプログラムは期待されたようには動作しない。 その理由を、専門用語を使って説明しなさい。

元のプログラムは、ポインタを利用した参照渡しのため、
inputPoint 関数内で読み込んだ値を、main 関数内でも利用できる。
しかし、修正したプログラムでは、値渡しのため、
scanfで読み込んだ値は inputPoint 関数内でしか参照できず、
中点の計算には利用できない。

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

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

整数を 10個標準入力から読み込んで、その最小値を求め、標準出力に書き出すプログラムを作成しなさい。エラー処理は無視してかまわない。

#include <stdio.h>

int main (void)
{
    int i, n, min;

    scanf("%d", &min);

    for (i=1; i<10; i++) {
        scanf("%d", &n);
        if (min > n) {
            min = n;
        }
    }

    printf("%d\n", min);

    return 0;
}

Q & A フォーラム (12 点)

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

warning: function returns address of local variable と出ましたが、なぜでしょうか。
ローカル変数は再度関数を呼ぶと上書きされるので、そのアドレスを返すことは意味がないです。
Program busy or waiting for too long! といわれたが、無限ループもないし、大した計算もやってない。
プログラムが更なる入力を待っているが、入力がもうないときにプログラムがそのまま待って、いつまでも終わらない。
too few arguments to function 'fwrite' にはどうやって対応すればよいでしょうか。
fwrite は四つの引数をとっています (書き出すデータのアドレス、データ項目一個のサイズ、データ項目の数、ファイルポインタ)。確認してください。
なぜか undefined reference to `_prinf' がでましたが、分かりません。
「prinf」という関数を使おうとしていますが、printf のではないでしょうか。
手元で cannot open output file a.exe: Device or resource busy になったが、助けてください。
a.exe はまだ実行中なので、コンパイラが上書きできません。(必要でしたらタスクマネジャーを使って) プログラムを終了してください。
Floating point exception (core dumped) の原因はなんでしょうか。
一番よくありそうな原因は 0 での割算です。

青山学院大学

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

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

式の評価 (12 点)

次のテーブルの式の値を計算しなさい。(ヒント: + が << より優先)

番号 番号
2+3*5 17 2+3*5 17
~ - 1 0 3 + 2 << 5 - 3 20
19 & 15 3 ~ (3 | ~ 15) 12
4 > 2 ? 13 : 9 13 0x3F - 0x24 27
55 / 8 6 53 % 7 4
15 >= 7 < 1 0 (int) 1350000E-5 13
15 ^ 27 20 20, 42, 37 37

オブジェクト指向とカプセル化 (6 点)

オブジェクト指向においてのカプセル化を C++ を例に使って詳しく説明しなさい。

カプセル化はオブジェクト指向の一番基本的な特徴。
カプセル化は、オブジェクトの持つデータ (メンバ変数)
は他のクラスからアクセスや変更ができないことである。
C++ では class で、struct と違って、メンバ変数は普通 private となる。
カプセル化によりプログラムのそれぞれの部分はお互い独立性が上り、変更が簡単になる。

変数の宣言 (10 点)

次の変数の宣言を書きなさい。

例: 実数 a: double a;

   10個の整数の配列 b: int b[10];

   80バイト分の文字列 12個の配列 c: char c[12][80];

   整数 16個の配列へのポインタ e: int (*e)[16];

   実数のポインタ 7個の配列 f: double *f[7];

   整数二つを引数にとり、実数を返す関数 (へのポインタ) 5個の配列 d: double (*d[5])(int, int);

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

この授業で一番分かりにくかったことを書きなさい。(何か書いたら 3 点)

@@@@

この授業で一番勉強になったことを簡単に説明しなさい。(何か書いたら 3 点)

@@@@

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

関数の再帰呼び出し (合計 15 点)

フィボナッチ関数 F(n) は次のように定義されている:
F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2) (n>2 のとき)

下記の 2つのプログラムは、どちらも標準入力から整数を 1つ読み込み、その整数に対応するフィボナッチ関数を計算し、表示する。 左側のプログラム (A) は関数の再帰呼び出しを利用して実装され、右側のプログラム (B) は再帰呼び出しを使わずに実装されている。

次の2つのプログラムの空欄を埋めなさい (5 点)

/* プログラム(A) */
#include <stdio.h>

long fibonacci (int n) {
    if ( n < 1   (@))
        return 0;
    else if ( n == 1  (@@))
        return 1;
    else
        return fibonacci(n-1) +
               fibonacci(n-2);
}

int main(void) {
    int n;
    if (scanf("%d", &n) == 1)
        printf("%ld\n", fibonacci(n));
    else
        printf("整数値を正しく読み込めません。\n");

    return 0;
}
/* プログラム(B) */
#include <stdio.h>

long fibonacci (int n) {
    int i;
    int fn2 = 0;   // F(n-2)
    int fn1 = 1;   // F(n-1)
    int fn;        // F(n)

    if (/* (@) の欄と同じ */)
        return 0;
    else if (/* (@@) の欄と同じ */)
        return 1;

    for (i=1; i<n; i++) {
        fn = fn1 + fn2;
        fn2 = fn1;
        fn1 = fn;
    }

    return fn       ;
}

int main(void) {
    /* プログラム (A) と同じ、省略 */
}

上の2つのプログラムの実行に関する次の文章の空欄を埋めなさい (5 点)

次の説明文が正しい場合は「○」を、間違っている場合は「×」を空欄に記入しなさい (5 点)

青山学院大学

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

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

動的メモリ (合計 15 点)

以下のプログラム断片で、関数 structCopy は、構造体 St の配列 array と要素数 asize を渡すと、動的にメモリを確保したあと、 array の先頭から asize 個をコピーし、複製したメモリの先頭アドレスを返す。

空欄を適切に埋め、関数を完成させなさい。 (10 点)

typedef struct {
    int num;
    char str[100];
} St;

St *structCopy(St array[]      , int asize)
{
    St *p, *head;

    if (!(head = (St *   ) malloc   (sizeof(St) * asize)))
	fprintf(stderr, "Not enough memory!\n"), exit(1);

    p = head;
    while (p - head < asize      ) {
	p->num = array->num;
	strcpy(p->str, array->str);
	array++;
	p++;
    }

    return head;
}

上記の関数 structCopy を使う際に、注意すべき点を述べなさい。(5 点)

structCopy で確保した領域が不要になったとき、 関数 free を使って、 その領域を解放し、 メモリリークを防止すべき点。

ゼロの仲間 (合計 16 点)

次の表を埋めなさい。(10 点)

名前定数の書き方
実数のゼロ 0.0 double
空の文字列 "" char *
ナル文字 '\0' char
整数のゼロ 0 int
ナルポインタ NULL void *

上記の表の定数を上記の表の型の変数へ代入するとき、エラーの場合と警告の場合を列挙しなさい。(4 点)

エラー: char * と void * から double へ、またはその逆
警告: char * と void * から int と char へ

上記の五種類のゼロの仲間のうち、偽とみなされるものはどれですか。(2 点)

空の文字列以外、全てのものが偽とみなされる。