問題の番号などが見えるようには最新のブラウザ (例えば Opera) が必要。 回答 (例) を見るにはスタイルシートを切り替える必要がある。

青山学院大学

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

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

関数の仕分け (9点)

次の関数が入力用の関数、出力用の関数、標準入出力用の関数、ファイル入出力用の関数、文字入出力用の関数、バイナリ入出力用の関数である場合に各欄に○、そうでない場合に×を記入しなさい。

番号 関数 入力用 出力用 標準入出力用 ファイル入出力用 文字入出力用 バイナリ入出力用
putc × × ×
scanf × × ×
fopen ×
pow × × × × × ×
fseek × ×
gets × × ×
printf × × ×
fwrite × × ×
fgets × × ×

用語の説明 (8点)

次の用語を簡単に説明しなさい。

番号 用語 説明
配列 同じ型の要素をある一定の数納められるデータ型の種類
宣言 変数や関数の存在、名前、型を知らせる
メンバ 構造体の「部品」
ヒープ 動的メモリに使われるメイン・メモリの部分
関数 引数を与えて呼び出すと決まった仕事をして、結果を戻り値で返すもの
定義 変数や関数の実際のメモリの用意、そして場合によって初期化
スタック 関数の引数、局所変数などに使われるメイン・メモリの部分
グローバル変数 プログラムの度の部分からでも使える変数

ポインタの応用 (3点)

次のプログラムは実行すると A という文字を出力する。空欄をコメントを参考にして埋めなさい。

#include <stdio.h>

int main (void) {
	char c;
	 char *p ;              /* ポインタの宣言 */

	c = 'A';
	p =  &c ;               /* ポインタの値の設定 */
	printf("%c\n", *p);	/* ポインタの指す値を表示 */
	return 0;
}

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

エラー探し (12点)

あるプログラムの想定されている出力は次のとおりである。表の中身はパスカルの三角形を横倒ししたもの。

        0      1      2      3      4      5      6      7      8      9     10
 0      1      1      1      1      1      1      1      1      1      1      1
 1      1      2      3      4      5      6      7      8      9     10     11
 2      1      3      6     10     15     21     28     36     45     55     66
 3      1      4     10     20     35     56     84    120    165    220    286
 4      1      5     15     35     70    126    210    330    495    715   1001
 5      1      6     21     56    126    252    462    792   1287   2002   3003
 6      1      7     28     84    210    462    924   1716   3003   5005   8008
 7      1      8     36    120    330    792   1716   3432   6435  11440  19448
 8      1      9     45    165    495   1287   3003   6435  12870  24310  43758
 9      1     10     55    220    715   2002   5005  11440  24310  48620  92378
10      1     11     66    286   1001   3003   8008  19448  43758  92378 184756

このプログラムは以下のとおりであるが、現在 12 個のエラーがある。入れ代えなどは一つのエラーと数える。エラーを見つけて直接プログラムに書き込んで修正しなさい。

#include <stdio.h>

double fac (int n)
{
    retaurn n<2 !? 1 : n * fact(n-1);
}

int main (void)
{
    int i, j=0;

    printf ("  ");
    for (i=0; i<=10; i++)
        printf (" %56d", i);
    printf ("\n");
    for (; j<11; ij++) {
        printf ("%2d'", j);
        for (i=0; i<11; i++)
            printf (" %6gd", (int)(fac(i+j)/fac(i)/fac(j)));
        printf ("\n");
        }
    }
}

整数と浮動小数点数 (4点)

上記のエラー探しの問題で fac 関数 (階乗関数) の戻り値の型は double である。その理由を簡単に説明しなさい。

階乗の引数が少しでも増えると戻り値が猛スピードで増える。機械によっては int, long など整数は4バイトしか使わないが、double はそれ以上の精密度を持っています。表の計算で割り算もあって、最終的にはそれほど大きい数字にはならないので、そこで int に戻すことができる。

式の評価 (8点)

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

番号 番号
2+3*5 17 2+3*5 17
3<<2 + 5 384 (int) 7.1e5 710000
200 / 20/5 2 (int)(6.03*6.99) 42
'G'-'N' -7 5678 ^ ~5678 -1
3 >= 12 && 7.5 < 7.7 0 4 > 3.7 ? 2.3 : 7 2.3

青山学院大学

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

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

構造体 (5点)

次のプログラムは学生情報が記載された構造体データを値渡しと参照渡しの二とおりで関数に渡して表示する。出力は次のようになる:

 値渡し: 15800000 例題花子 76.5
参照渡し: 15800000 例題花子 76.5

このプログラムの空欄を埋めなさい。

#include <stdio.h>

typedef struct {    /* 学生情報 */
    int no;         /* 学生番号 */
    char name[20];  /* 氏名     */
    double average; /* 平均点   */
} Student;

void disp1 (Student st)
{
    printf("  値渡し: %d %s %f\n", st.no, st.name, st.average);
}

void disp2 ( Student *st )
{
    printf("参照渡し: %d %s %f\n",  st->no ,  st->name ,  st->average );
}

int main (void)
{
    Student seito = {15800000, "例題花子", 76.5};

    disp1(seito);    /* 値渡しで学生情報を渡して表示 */
    disp2( &seito ); /* 参照渡しで学生情報を渡して表示 */
    return 0;
}

インクリメント (4点)

左下のプログラムを実行した場合の右下の実行結果について空欄を記入しなさい。

#include <stdio.h>
int main(void){
    int a=3, b, c;
    ++a;
    b = ++a;
    printf("a = %d, b = %d\n", a, b);
    a = 3;
    a++;
    c = a++;
    printf("a = %d, c = %d\n", a, c);
    return 0;
}

実行結果

a =  5 , b =  5 
a =  5 , c =  4 

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

メモリ配置 (合計13点)

メモリ配置表の生成 (5点)

左下のプログラムを実行すると右下の実行結果になる。

#include <stdio.h>

typedef struct {
    char c;
    int i;
    short s;
} Test;

int main ()
{
    Test a, b;
    printf ("Address of a.c: %p\n", &a.c);
    printf ("Address of a.i: %p\n", &a.i);
    printf ("Address of a.s: %p\n", &a.s);
    printf ("Address of b.c: %p\n", &b.c);
    return 0;
}

実行結果

Address of a.c: 0x22eeb0
Address of a.i: 0x22eeb4
Address of a.s: 0x22eeb8
Address of b.c: 0x22eea0

変数 ab の全てのメンバのメモリ配置表を作成しなさい。(各枠に使われていたら使っているメンバー (例: b.i)、使われなかったら - を記入しなさい。)

メモリ配置表
  xxx0xxx1 xxx2xxx3 xxx4xxx5 xxx6xxx7 xxx8xxx9 xxxAxxxB xxxCxxxD xxxExxxF
0022eeay b.c- -- b.ib.i b.ib.i b.sb.s -- -- --
0022eeby a.c- -- a.ia.i a.ia.i a.sa.s -- -- --

構造体 Test をメモリがもっと効率よく使われるように書き直しなさい (3点)。

typedef struct {
    int i;    
    short s;  
    char c;   
} Test;

新しく定義した構造体 Test の場合に想定されるメモリ配置を書きなさい (5点)。

メモリ配置表
  xxx0xxx1 xxx2xxx3 xxx4xxx5 xxx6xxx7 xxx8xxx9 xxxAxxxB xxxCxxxD xxxExxxF
0022eeay b.ib.i b.ib.i b.sb.s b.c- a.ia.i a.ia.i a.sa.s a.c-
0022eeby -- -- -- -- -- -- -- --

ポインタの用途 (10点)

ポインタの五つの用途を述べ、簡単に説明しなさい (例を使ってもよい)。

番号 用途 説明
低レベルのアドレス操作 デバイス割り当てなどに使う。
動的メモリの管理 malloc などでメモリを用意し、そのアドレスをポインタで取っておく。
配列の処理の効率化 配列の a[i] の代わりに移動するポインタを使う。
参照 例えば引数の関数への参照渡し。
間接 例えば複数の並び換えを前もって作っておいて、データをコピーしなくてポインタを使う。

青山学院大学

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

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

文法の変更 (8点)

次のプログラムの部分を「使用禁止」欄の演算子,予約語又は関数を使わずに記述しなさい。

(「...」は任意の部分を 表す。abint 型の変数で、pint * 型の変数。)

番号 変更前 使用禁止 答え
i++; ++ i += 1;
while (a > 12) { ...; } while for ( ; a > 12; ) { ...; }
if (a || b ) { ...; } || if (a) { ...; } else if (b) { ...; }
p[b] [ ] *(p+b)
a * 64 * a << 6
b * 65 * (b<<6) + b
a = getchar(); getchar a = getc(stdin);
*p * p[0]
if (a>b) i=6; else i=5; > if (a<=b) i=5; else i=6;

入力の危険な関数 (5点)

入力関数の場合、入力や使い方によってプログラムの不具合や暴走、そしてウィルスの侵入につながる恐れのある危険な関数がある。この関数やその使い方を列挙し、危険性の理由を説明し、対策を述べなさい。

危険な関数は gets (標準入力から一行読み込む) と scanf である。scanf の場合に危険なのは %s です。入力用に用意されたメモリの長さが関数に伝わらないので、そのメモリを超えて別のところに書き込むことになる可能性がある。それによってプログラムの急な停止 (segmentation fault)、 暴走、ウィルスの侵入もあり得ますので、絶対使わない方がよい。その代わりに fgets を使った方がよい。

動的メモリの定石 (6点)

C のプログラム中によく次の一句が見える (arrayint 型へのポインタ):

if (!(array = (int *) malloc(sizeof(int) * 10)))
    printf("Not enough memory!\n"), exit(1);

この句の一つ一つの部分を説明しなさい。

番号部分説明
if (!( メモリが足りるかのテスト
array = 新しいメモリへのポインタの代入
(int *) 適切なポインタ型への変換
sizeof(int) * 10 必要なメモリの大きさの計算
printf 関数の呼び出し メモリが足りなかったことを書き出す
exit 関数の呼び出し メモリが足りなかったのでプログラムを終了

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

合計の色々な計算 (5点)

下記のプログラムの一部に、           の行にそれぞれの文を当てはめたとき、それぞれの出力結果を答えなさい。

int i, sum;

sum = 0;
for (i=0; i<10; i++) {
              
}
print ("%d\n", sum);
番号           の一行の文出力
sum; 0
sum++; 10
sum += i; 45
sum += i/2; 20
sum += i/2.0; 20
sum += sum+i;   1013  

ユークリッドの互助法 (5点)

2つの整数の最大公約数を求めるにはユークリッドの互助法がある。これは次の再帰的な定義に基づく方法である。2つの整数 mn の最大公約数は次の通りになる:

  1. mn が等しい場合、最大公約数は m
  2. m > n の場合、最大公約数は nm-n の最大公約数に等しい。
  3. m < n の場合、最大公約数は mn-m の最大公約数に等しい。

次の最大公約数を計算する関数 gcd の空欄を埋めて完成しなさい。

int gcd (int m, int n)
{
    if (m == n)
        return m;
    else if ( m > n )
        return  gcd(n, m-n); 
     else 
         return gcd(m, n-m);  
}

ビットごと演算 (12点)

次の表を穴埋めしなさい。

番号 a b c a & b a | c c << a (a | b) & (c>>1)
2 4 5 0 7 20 2
3 5 9 1 11 72 4
6 17 63 0 63 4032 23
4 0 128 0 132 2048 0

青山学院大学

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

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

ポインタの演算 (6点)

左下のプログラムを実行すると右下の実行結果のようにそれぞれの単語がその文字数と共に出力される。空欄を埋めなさい。

#include <stdio.h>
int main ( void ){
    char *name[10] = { "One", "Two",
        "Three", "Four", "Five", "Six",
        "Seven", "Eight", "Nine", "Ten" };
    char **p, **endp;
    int i;
    p = name;
    endp = name+10;
    for (p = name; p < endp; p++) {
        for (i=0; (*p)[j] != '\0'; i++) ;      
        printf("%s %d\n", *p, i);        
    }
    return 0;
}

実行結果

One 3
Two 3
Three 5
Four 4
Five 4
Six 3
Seven 5
Eight 5
Nine 4
Ten 3