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

青山学院大学

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

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

式の評価 (12点)

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

番号 番号
2+3*5 17 2+3*5 17
27 & 35 3 7 > 15 ? 20 : 17 17
33 | 12 45 (int)(3.5 * 7) 24
22 ^ 63 41 (int)3.5 * 7 21
25 / 7 3 22, 23, 99 99
a = 13 13 28 - 13+4 19
13 << 5 416 63 >> 3 7

文法の変更 (20点)

次のプログラムの部分を「使用禁止」欄に書いたものを使わずに記述しなさい。「...」は任意の部分を表す。

番号 変更前 使用禁止物 答え
i--; -- i -= 1;
pt[x] [] *(pt+x)
p[a[b]] [] *(p+*(a+b))
st.mem . (&st)->mem
int1 / 64 / int1 >> 6
a->b -> (*a).b
a[3].b []. (a+3)->b
if (a>b && c>d) ... >&& if (!(a<=b||c<=d)) ...
for (i=0; i<50; i++) {...} for i=0; while (i<50) {... i++; }
~a|b | ~(a&~b)

DRY (6点)

いいプログラムの一つの条件としては DRY がある。DRY の意味と役割について説明しなさい。

DRY は don't repeat yourself、つまり「繰り返しに言うな」という意味です。 プログラムを簡潔で分かりやすいようにするには同じ構文や似たような構文を避け、一つのことは一回だけ書くというのは 大事です。そのため関数を使ったり、if 文や switch 文から共通するものを外に出したり、 書く人間ではなくプログラムが繰り返しすればいいです。

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

英語の用語 (30点)

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

recursion 再帰、関数が同じ (仕事をしている) 関数を再び呼ぶこと

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

heap ヒープ、動的メモリに使われるメモリの部分

variable 変数、型と名前が付いたメモリの部分; 箱みたいなものと考えてよい

array 配列、同じ型のデータがある決まった数並んでいる型

definition 定義、変数や関数の実体を知らせるもの

type 型、データや変数の種類で、それによって使える演算などが決められている

structure 構造体、複数の異なる名前と型のデータをまとめた型

function 関数、ある処理をまとめて、名前が付いた部分。引数と戻り値がある。

member メンバー、構造体の「部品」

declaration 宣言、変数や関数の存在と型の知らせ

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

output 出力、プログラムからデータを、例えば画面やファイルに、外に出すこと

assignment 代入、変数に値を入れること; 前に入った値が上書きされる

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

入出力の種類 (8点)

C プログラム言語の場合にテキスト入出力とバイナリ入出力があります。その違いを詳しく (目的、使われる関数、特殊文字への対応、速度、ファイルの特徴など) 説明しなさい。

テキスト入出力は人間が使いやすいように、しかも違う仕組みの計算機の間での交換がしやすいように、 出力の時に内部のバイナリデータを文字に変える。入力はその逆。バイナリ出力はメモリ内のデータをそのまま、一ビットも変更しないで出力する。入力はその逆。 テキスト入出力は printf/scanf, putc(har), getc(har), fgets, puts, fputs 等など関数が多い。バイナリ入出力は fread と fwrite です。バイナリ入出力がうまく行くようには fopen のときに mode を "rb" 又は "wb" にしないといけない。しないと改行など特殊文字が変更されるおそれがあります。速度はバイナリ入出力の方はデータを変更しないので速いです。 ファイルはテキスト入出力の場合は普通に読めますが、バイナリ入出力の場合は読めない部分が多い。文字化けにも見えるが、文字化けでは無い。

青山学院大学

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

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

関数 strlen の使い方 (20点)

下記の toUpper 関数で strlen 関数が使われている。 toupper はある文字が小文字でしたらこれを大文字にする関数です。

#include <ctype.h>
#include <string.h>

int toUpper (char string[])
{
    int i;

    for (i=0; i<strlen(string); i++)
        string[i] = toupper(string[i]);
}

strlen の使い方の問題点 (6 点)

上記の関数内の strlen の使い方の問題点を述べてください。(ヒント: toUpper に渡される文字列がどんどん長くなるとどうなるか。)

strlen は文字列のあたまから最後の NULL 文字まで文字を数えているので、文字列の長さに比例して時間がかかる。 for 文も文字の数だけ回数がある。そうすると文字列の長さが倍になると toUpper 関数の使う時間がおよそ4倍になる。 4倍はたいしたことではないが、1000 文字の場合百万倍、一万文字の場合一億倍なのでどんどん遅くなります。

簡単な解決案 (4点)

上記の問題を簡単に解決するにはどうすればよいのか書いてください。 (ヒント: strlen は使ってもいいですが)

文字列の長さは代わらないので、例えば int length=strlen(string); でとっておいて、for 文の strlen(string) の代わりに length を使う。

strlen 無しの書き直し (10点)

上記の toUpper 関数を strlen 関数を使わないように書き直してください。そのとき、できるだけ C らしくポインタを使って書いてください。

#include <ctype.h>
int toUpper (char *sp)
{
    for ( ; *sp; sp++)
        *sp = toupper(*sp);
}

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

文字列にスペースを挿入するプログラム (合計 18点)

下記のプログラムは入力から一行読んで、入力の文字 (実はバイト) の間に半角スペースを挿入し、結果を出力する。

実行例:

文字列を入力してください。
Aoyama
結果: A o y a m a

プログラムの完成 (12点)

プログラムの空白を埋めてプログラムを完成しなさい。

#include <stdio.h>

char *strspace(char *s);

int main (void)
{
    char str[100];
    int len;

    printf("文字列を入力してください。\n");
    fgets(str, 100, stdin);
    len = strlen(str);
    if (str[len] == '\n')
        str[len] = '\0';
    printf("結果: %s\n", strspace(str));
    return 0;
}

char *strspace(char *s)
{
    char *head,*ptr;

    head = (char *)malloc(strlen(s)*2+1);
    ptr = head;
    while(*s !='\0'){
        *(ptr++) = *(s++) ;
        *(ptr++) = ' ' ;
    }
    *ptr = '\0' ;
    return head;
}

malloc の問題点 (6点)

上記のプログラムで malloc 関数が使われている。上記の使われ方は、特にもっと大きいプログラムの場合大切な、二つの問題がある。その問題と解決方法を説明しなさい。

一つ目の問題としては、malloc は必ずしもメモリを見つけない可能性があります。malloc の戻り値が NULL の場合、エラーメッセージを出してプログラムを終了する必要があります。

二つ目の問題は、malloc されているのに free されてないメモリがあります。だれ (どの関数) が free する責任があるのかを決めて、実際に free する必要があります。

青山学院大学

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

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

文字列と配列 (8点)

次のプログラムは 2 通りの方法で文字列を格納しを表示している。

#include <stdio.h>

int main(void)
{
    char str[3][7] = {"ABC", "DEFGHI", "JK"};
    char *p[3] = {"ABC", "DEFGHI", "JK"};
    int i;

    /*  第一の方法 */
    for (i = 0; i < 3; i++)
        puts(str[i]);

    /* 第二の方法 */
    for (i = 0; i < 3; i++)
        puts(p[i]);

    return 0;
}

二つの方法の相違点を説明しなさい。

第一の方法は二次元配列に文字列を格納し、その文字列を表示している。 第二の方法は文字列ポインタの配列に文字列のアドレスをそれぞれ格納した上で、ポインタの指す文字列を表示している。

学生の成績表 (合計 30点)

次ページに学生の成績を処理するプログラムがあります。入力のデータは名前、そして整数値による数学と英語の得点がスペースで区切られた形式でファイルに格納されている (下記参照)。 getAverage関数は、与えられたデータから、数学と英語の平均点を計算するようになっている。

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

相澤信二 65 80
相羽高徳 80 75
赤松一 55 60
秋元竹夫 85 75
浅野健一 95 90
浅見次郎 60 63
安食伸太郎 50 85
芦部光義 75 60
有馬卓 80 75
有田英世 65 60

(次ページに続く)

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

下記のプログラムの        のところを埋めてプログラムが正しく動作するようにを完成しなさい。

#include <stdio.h>
#include <stdlib.h>

#define STUDENT_NUM 10

typedef struct {
    char name[20];
    int  math;
    int english;
} Person;

void readFile( char * fileName , Person *data, int num) {
    FILE *fp;
    int i;
    fp = fopen(fileName, "r");
    if (fp == NULL) {
        printf("ファイル %s をオープンできません\n", fileName);
        exit(3);
    }
    for (i = 0; i < num; i++) {
        fscanf(fp, "%s %d %d", data[i].name , &data[i].math , &data[i].english );
    }
    fclose(fp);
}

void getAverage( Person * data, int num, double *mathAve, double *engAve) {
    int i;
    double mathTotal = 0.0;
    double engTotal  = 0.0;

    for (i = 0; i < num; i++) {
        mathTotal = mathTotal + data[i].math    ;
        engTotal  = engTotal  + data[i].english ;
    }
    *mathAve = mathTotal / num;
    *engAve  = engTotal / num;
}

void main() {
    Person student[STUDENT_NUM];
    int i;
    int math, english, total;
    double mathAve, englishAve;

    readFile("scores.txt", student, STUDENT_NUM);
    getAverage(student, STUDENT_NUM, &mathAve, &englishAve);

    printf("数学平均点 : %lf\n", mathAve);
    printf("英語平均点 : %lf\n", englishAve);
    printf("=======================================\n");
    printf("Num 氏名                 数学 英語 合計\n");
    printf("---------------------------------------\n");

    for (i = 0; i < STUDENT_NUM; i ++) {
        math = student[i].math;
        english = student[i].english;
        total = math + english;
        printf("%3d %-20s %4d %4d %4d ", i+1, student[i].name, math, english, total);

    }
}

(次ページに続く)

青山学院大学

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

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

入力形式の変化 (5点)

データ中で苗字と名前をスペースで区切るとプログラムは正しく動作しない。その理由を説明しなさい。

(f)scanf の %s は空白類 (whitespace、すなわち空白、タブ、改行) を含まない文字列を読み込む。スペースのところで名前が終わりと思って、次に数学の成績を読もうとしているところで、数字を期待しているのに文字を読もうとしている。

入力の長さ (5点)

長い名前が入力されると上記のプログラムがエラーを起こす可能性がある。その原因と理由を説明しなさい。

(f)scanf の %s や gets では読み込む文字列の長さの制限がない。名前が 30 文字でしたら30文字読み込みます。しかし20文字の場所しか用意されてなくて、残りの10文字が別のところを勝手に上書きし、 その結果プログラムがどうなるのか全く保証が無い。

変数の有功範囲 (合計 8点)

下記左側には変数の有効範囲を確かめるためのテストプログラム、右側にはその実行結果がある.実行結果の空欄を埋めよ。

#include <stdio.h>

void func1(void);
void func2(int no);
int no = 123;

int main(void)
{
    int no = 456;
    printf("main-1:no  = %d\n", no);
    func1();
    func2(789);
    printf("main-2:no  = %d\n", no);
    return 0;
}

void func1(void)
{
    int i;
    printf("func1-1:no = %d\n", no);
    for (i = 1; i <= 3; i++) {
        int no = i * 5;
        printf("func1-2:no = %d\n", no);
    }
    printf("func1-3:no = %d\n", no);
}

void func2(int no)
{
    printf("func2:no   = %d\n", no);
}
main-1:no  = 456
func1-1:no = 123
func1-2:no =   5
func1-2:no =  10
func1-2:no =  15
func1-3:no = 123
func2:no   = 789
main-2:no  = 456

青山学院大学

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

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

シェルの使い方 (合計 14点)

シェルで使える記号 (8点)

C プログラムをコンパイル・実行・テストするときにはシェル (PuTTY/Cygwin) が色々便利な機能・記号を提供してくれる。下記の記号を、使い方を含めて説明しなさい。

  1. "<" 右側に指定したファイルからキーボードの代わりに左側のプログラムの標準入力にリダイレクト
  2. "&&" 「かつ」。左のコマンドが成功しなかったら右のコマンドは実行されない。
  3. "|" 二つのコマンドを出力と入力でつなぐ。左のコマンドの標準出力を右の標準入力に流し込む。
  4. ">" 出力のリダイレクト。左のコマンドの標準出力は画面ではなく右のファイルに入る。

シェルの連続コマンド (6点)

上記の記号をできるだけ全てを使って、授業で使った連続コマンドを書いて説明してください。

gcc 07A3.c && ./a < test-in.txt | diff - test-out.txt > diff-result.txt

07A3.c というプログラムを gcc という C コンパイラでコンパイルして、成功したらできた a.exe プログラムを test-in.txt からの入力で実行し、その出力を diff コマンドで test-out.txt と比べ、比較の結果を diff-result.txt に取っておく。

文字コード (6 点)

文字コードによって扱える文字や文字ごとのバイト数が異なります。次の表に文字が扱えない場合に -、扱える場合文字ごとのバイトの数を記入しなさい。

 Shift_JIS (CP-932)UTF-8
アラビア文字 (ابةتث)-2
半角片仮名 (アイウエオ)13
ハングル (韓国の文字、가각갂갃간)-3
ローマ字 (abcde)11
片仮名 (アイウエオ)23
ヘブライ文字 (אבגדה)-2
ギリシア文字 (αβγδε)22
平仮名 (あいうえお)23
「記号付き」ローマ字 (âéïõü)-2
漢字 (亜唖娃阿哀)23