(5/24) malloc 関数に関する説明を修正しました.

C 言語のプログラムは,main 関数内に動作させたい内容を記述すればよいが,プログラムの規模が大きくなるにつれて,main 関数の内容が膨大になってしまう.このような場合,適当な大きさの関数に分割してプログラムを書くことが多い.たとえば,繰り返し計算する部分を関数として実装することで,別の箇所から何回も呼び出すことができる.ここでは,関数を使ったプログラムを作成しよう.

問題

標準入力から実数のデータ列を読み込み,その数の出現個数のヒストグラムを書き出すプログラムを作成せよ.

プログラムは以下の仕様を満たすこと(以下の仕様を満たしたプログラムに5点を加点する).

実行例

入力ファイル (data.txt) の例

10
2.6 3.4 3.2 2.4 8.8 4.3 9.7 2.2 4.1 5.1

リダイレクション < を用いて data.txt の内容を標準入力から読み込む.

$ ./a.out < data.txt

data.txtに対する実行結果の例(> は標準出力を示し, は半角スペースを示す.)

>  0⊔
>  1⊔
>  2⊔**
>  3⊔***
>  4⊔**
>  5⊔*
>  6⊔
>  7⊔
>  8⊔
>  9⊔*
> 10⊔*
> 11⊔
> 12⊔
> 13⊔
> 14⊔
> 15⊔
> 16⊔
> 17⊔
> 18⊔
> 19⊔
> 20⊔

ヒストグラム

ヒストグラムは,片方の軸に度数,もう片方の軸に階級(ビン)をとったグラフの一種である.データの頻度を視覚的に調べるときに用いられ,度数分布とも言われる. 下図は,Wikipedia で例として使われているヒストグラムである.これは,アメリカで 1973 年から 1978 年にかけて事故死した人数を月毎に集計したものであり,横軸は各月に事故死した人数を 500 人毎に区切った階級を,縦軸は各階級に属する月の数(=度数)を表している.

ヒストグラム

ヒント

メモリ領域の動的確保

C言語では, int a[5]; のように, 配列を宣言して使用するためには具体的に要素数を指定する必要があります. しかしこの仕組みは,今回の課題のように,配列の要素数が入力等から得られるまで不定の場合には不都合が生じてしまいます. 例えば,int a[5]; として用意した配列に10個の配列要素を読み込ませることはできませんし, かといってあらかじめ int a[500]; のように多めに配列を用意していても,実際に入力される要素数が10や20だった場合は貴重なメモリ容量の無駄遣いになってしまいます.

C言語ではこのようにプログラムを実行するまで配列の要素数が決まらない場合に 必要に応じて必要なだけ配列要素を確保する malloc(えむあろっく,まろっく)と言う関数が用意されています. 今回の課題のように標準入力から配列の要素数が 10 と入力された場合は

int *x;
x = ( int * ) malloc ( 10 * sizeof ( int ) );

のように malloc 関数を使用します. malloc 関数は確保した領域の先頭ポインタを返すため、[] 演算子を用いることで x[0],x[1],x[5] 等のようにポインタ x をあたかも配列 int x[10]; と宣言した場合のように扱うことができます.</br> (malloc関数を用いる場合は必ず確保に失敗した場合のエラー処理と free による解放処理を行ってください.)

配列を引数にとる関数

C言語では以下のように記述することで配列を引数とする関数を作ることができます.

例えば配列要素の総和を計算するプログラムは

#include <stdio.h>

int func ( int a[], int n ); // または int func ( int *a, int n )

int main ()
{
	int sum;
	int a[5] = {1,2,3,4,5};

	sum = func ( a, 5 );
	printf ( "%d\n", sum );

	return 0;
}

int func ( int a[], int n ) // または int func ( int *a, int n )
{
	int i, ans = 0;
	for ( i=0; i < n; i++ )
	{
		ans += a[i];
	}
	return ans;
}

のように記述できます.どちらの記述例でも動作しますが,通常の配列とポインタ型を区別するために,int a[5]; のように宣言した配列を引数とする場合には(1)を,int *a = (int*)malloc(SIZE); のように動的に確保した配列を使用する場合には(2)の記述法を用いることが推奨されます.