第8回演習の解答例

関数を用いなければ,ファイルの取り扱いは理解できているようですね。前回演習の関数を利用しない場合について,見直しましょう。

プログラムを作成する際,プログラム全体を一度に書き上げるのではなく,小分けして,その都度結果を画面に表示してみて,うまく動いているのか,確認しながら,次のステップへと進めていくと,より短時間でプログラムを完成させることができます。一度に書き上げると,どこまで動いているのか,どこが間違っているのか,わからなくなります。

 

(1)試験結果のデータファイルtestdata.txt(右クリック-->[対象をファイルに保存] )を読み込んで,大きい順に並べ替え,別のファイルに出力するプログラムを,第6回(3)を元にして作成せよ。

(ステップ1)関数を用意しないで,main関数だけで作成してみる。

#include <stdio.h>

void main()
{
    int a[512], i, j, n=0, tmp;
    FILE *fp;

    fp = fopen("testdata.txt", "r");

    while(!feof(fp) && n < 512){
        fscanf(fp, "%d", &(a[n]));
        n++;
    }

    fclose(fp);    /* fclose()は,ループの中に入れない */
    n--;    /* どうして,nから1引くのか説明できますか? */

    /* 並べ替えは,もう大丈夫ですね */
    for(i=0; i<n-1; i++){
        for(j=i+1; j<n; j++){
            if(a[i] < a[j]){
                tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
            }
        }
    }

    fp = fopen("result.txt", "w");

    for(i=0; i<n; i++){
        fprintf(fp, "%d¥n", a[i]);
    }

    fclose(fp);    /* このfclose()も,ループには入れないよ */
}

第9回:関数の徹底理解 (6/29)

関数,特にアドレス参照になると,急に自信がなくなるようです。今日中にきちんと理解しよう。


ポインタ変数の基本

int a=10, b;

これは普通の整数型変数a,bの宣言。

int *p;

ポインタ変数pの宣言。ポインタの指す型を指定し,変数名の前に(アスタリスク)をつける。

p = &a;

アドレスを格納するポインタ変数pに,int型変数aのアドレスを代入。のアドレスを取り出すには,変数名の前に&をつける。

b = *p;

ポインタ変数に格納されているアドレスの示す先に格納されているを取り出すには,ポインタ変数の前に*をつける。
ここではポインタ変数 p の指す変数(ここでは変数 a )の値を,普通の整数型変数 b に代入。


クイズ

(1)値参照はきちんと理解できているか,下記のプログラムの実行結果を答えなさい。

#include <stdio.h>
int test(int a)
{
    a += 1;
    return (a);
}

void main()
{
    int a=1, b;

    b = test(a);

    printf("a = %d, b = %d ¥n", a, b);
}

値参照による関数の呼び出しでは,呼び出し元から関数へ変数の値がコピー,複製されて渡される。

値参照

 

(2)では,アドレス参照の場合はどうなるか,下記のプログラムの実行結果を答えなさい。

#include <stdio.h>
int test2(int *a)
{
    *a += 1;
    return *a;
}

void main()
{
    int a=1, b;

    b = test2(&a);
    
    printf("a = %d, b = %d¥n", a, b);
}

アドレス参照

 

(3)配列とポインタの関係を理解しているか確認しよう。下記のプログラムの実行結果を答えなさい。


#include <stdio.h>
 
void main()
{
    int a[5] = {10, 20, 30, 40, 50};
    int b;
    int *p;
 
    p = &a[0];
 
    printf("はじめの値は,%d です¥n", *p);
 
    p += 2;
 
    printf("次の値は,%d です¥n", *p);
 
    b = *(p-1);

    printf("b の値は,%d です¥n", b);  
}

(4)そして,最後に関数への配列の受け渡し。配列の中身を一つずつ渡していては,配列を使う意味がない。メモリの節約のためにも,配列を関数へ渡すときには,アドレス渡しを用いる。

実数型配列の要素をキーボードから10個入力する,平均値を算出する,配列の中身を表示する各関数を作成せよ。

#include <stdio.h>

void input(float *p, int n)
{
    int i;

    for(i=0;i<n;i++){
        printf("data[%d]=",i);
        scanf("%f",p+i);
    }
}

float average(float *p, int n)
{
    int i;
    float ave=0.0;

    for(i=0;i<n;i++) {
        ave += *(p+i);
    }

    ave /= n;

    return ave;
}


void output(float *p, int n)
{
    int i;

    for(i=0;i<n;i++){
        printf("data[%d]=%f¥n",i,*(p+i));
    }
}

void main()
{
    float data[10];

    input(data,10);                            /* 10個の要素を入力する関数*/

    printf("平均値 = %f¥n",average(data,10));   /* 平均値を算出する関数 */


    output(data,10);                           /* 配列の中身を出力する関数 */
}

演習課題

(1)試験結果のデータファイルtestdata.txt(右クリック-->[対象をファイルに保存] )を読み込んで,大きい順に並べ替え,別のファイルに出力するプログラムを,第6回(3)を元にして作成せよ。

(ステップ2)ファイルからのデータ読み込み,書き出し,並べ替えを下記のように関数にし,main関数をすっきりさせる。


#include <stdio.h>

/*ファイル入力の関数 ファイル名と配列のアドレスを受取,データの読み取り個数を戻す*/
_____ load(_____)	
{
	 ****** 
}

/*並べ替えの関数*/
_____ sort(_____)
{
	 ****** 
}

/*ファイル出力の関数*/
_____ store(_____)	
{
	 ****** 
}

/*メイン関数*/
void main()
{
    int data[256];  /* データ用配列 */
    int n;         /* データ数 */

    n = load("testdata.txt", data);  /* ファイルからデータを配列に読み取り,データ数を戻す */  
    sort(data, n);   /* 配列を並べ替える */
    store("result.txt", data, n);  /* 結果を保存する */
}

(2)次に各点数に順位を付けて,csvファイルに保存するプログラムを作成せよ。

注意1:ただし,同じ点数の者が2名いたら,次の点数の者の順位は,一つ飛ぶ。 たとえば,47点が53位で,47点の者が2名いたとすると,46点の者は,55位となる。
注意2:結果についてはデータと順位両方をファイルに書き込む。 csvファイルに書き込む時,fprintf関数における値の型を書く所をカンマ区切りすることによってセルの列を区別することができる。

CSVファイルの中身の例:

100,1
99,2
98,3
98,3
97,5

....

(3)各点数の偏差値を求めファイルに保存しなさい。