プログラミング入門2 - 学術情報センター

プログラミング入門2
第11回
情報工学科 篠埜 功
今日の内容
• malloc, callocによる動的な領域確保について
動的な記憶域確保
静的(static)な記憶域確保
• これまでの方法
– 配列の要素数は固定。
– あらかじめ大きめの配列を確保しておく必要が
あった。
• 今回紹介する方法 動的(dynamic)な記憶域確保
– 問題に応じて、適切なサイズの配列(記憶域)を
確保するには、プログラムの実行時に確保を行
う必要がある。
– 余分なメモリの使用を避けることができる。
静的(static) --- プログラムのコンパイル時
動的(dynamic) --- プログラムの実行時
ヒープ領域(heap)
• プログラムからはヒープ領域(heap)を用いることがで
きる。
• ヒープ領域を使うには、mallocあるいはcallocという
ライブラリ関数で領域を確保する。使い終わったら、
freeというライブラリ関数で解放する。解放すること
により、それ以降のmallocあるいはcallocで再利用
可能になる。
(注意)ここでいうヒープ領域は、データ構造の授
業で習う木構造のヒープとは関係がない。
calloc関数
• ヒープ領域から実行時に記憶域を確保する。
• calloc関数を使うためにはstdlib.hをインクルードする必
要がある。
• 返り値はvoidへのポインタ型(実際に使う型にキャスト
して使用する)
• 引数は、データ型のサイズ(第2引数)と、その個数(第
1引数)。
• データ型のサイズは、sizeof (型式) で取得できる。
ヘッダ
#include <stdlib.h>
形式
void * calloc (size_t n, size_t size);
解説
大きさがsizeであるn個のオブジェクトの領域を確保する。
返り値
領域確保に成功した場合は、その領域の先頭へのポインタを返
し、失敗した場合は、空ポインタ(NULL)を返す。
例(打ち込んで確認)
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *p;
int型1個分の記憶域をヒープ
p = (int *) calloc (1, sizeof (int));
領域から割り当てる
if( p == NULL )
printf (“記憶域の確保に失敗しました。\n");
else {
*p = 15;
printf ("*p = %d\n", *p );
}
return 0;
}
解説
• calloc関数による記憶域の動的な確保
int * p;
p = (int *) calloc (1, sizeof (int) );
int型ポインタ変数を宣言
int * p;
calloc関数呼び出し時に領域が確保され、
その先頭へのポインタがpに代入される。
p = (int *) calloc (1, sizeof (int) );
500番地
p
p
sizeof演算子
sizeof演算子は、型式を引数にとる。評価結果は、その型
のサイズである。
構文
sizeof (型式)
意味
sizeof (t) の評価結果はtのサイズである
型式は、int, double, char等の基本型、int [3]等の
配列型、struct {int px; int py} 等の構造体型、int *
等のポインタ型、 typedefで定義した型名などであ
る。詳しくは教科書あるいは規格書を参照。
例(打ち込んで確認)
#include <stdio.h>
typedef struct {
int x;
int y;
} point;
int main (void) {
printf ("int: %d\n", sizeof(int));
printf ("int[3] : %d\n", sizeof(int[3]));
printf ("struct {int x; int y;} : %d\n",
sizeof(struct {int x; int y;}));
printf ("point: %d\n", sizeof(point));
printf ("point *: %d\n", sizeof(point *));
return 0;
}
void へのポインタ
calloc関数の返り値の型はvoid *である。
p = (int *) calloc (1, sizeof (int) );
calloc関数の返り値 → void * 型 (voidへのポインタ型)
int, char, double, 構造体 など、さまざまな型のための領
域を確保する際にcalloc関数が用いられるので、void *型
で返している。
使うときには、calloc関数の返り値を実際に使うポインタ
型にキャストしてから使用する。
上の例では、int * 型にキャストしている。
free関数 : 記憶域の解放
• 動的に確保した記憶域は、不要になった時点
でfree関数を呼び出して解放する。それに
よって、それ以降のcallocやmallocの呼び出し
で再利用可能な状態になる。
ヘッダ
#include <stdlib.h>
形式
void free( void *p );
解説
pが指す領域を開放する。但しpがNULLであれば何
も行わない。
例(打ち込んで確認)
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *p;
p = (int *)calloc( 1, sizeof(int) );
if (p == NULL)
printf ("記憶域の確保に失敗しました。\n");
else {
*p = 15;
printf("*p = %d\n", *p );
free(p);
}
return 0;
}
記憶域解放
free(p);
p = (int *)calloc( 1, sizeof(int) );
記憶域確保
確保した領域へ値を書き込む例(打ち込んで確認)
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int * p;
p = (int *) calloc (1, sizeof(int));
if(p == NULL)
printf ("記憶域の確保に失敗しました。\n");
else {
printf ("整数を入力して下さい:");
scanf ("%d", p);
printf ("*p = %d\n", *p);
}
return 0;
}
1次元配列の動的確保
• 配列宣言の例
int x[10];
配列の要素数は定数式でなければならない。
要素数を変数とすることは1990年のISO規格
では許されていない。
(注)1999年のISO規格(C99)では許されている。
実行時に領域を確保することにより、適切な長
さの配列を用いることができる。
例(打ち込んで確認)
#include <stdio.h>
#include <stdlib.h>
int main (void) {
int no, i=0;
int * p;
printf("確保する配列の要素数:");
scanf("%d", &no);
p = (int *) calloc (no, sizeof (int));
if (p == NULL)
printf ("記憶域の確保に失敗しました。\n");
else {
while (i < no) {
p[i] = i; i=i+1;
}
i = 0;
while ( i < no ) {
printf("p[%d] = %d\n", i, p[i] );
i = i + 1;
}
/* 続き */
free (p);
}
return 0;
}
p[0]
sizeof(int) * 5
p[1]
p[2]
p[3]
p[4]
p
あたかも
int p[5];
と宣言された配列が存在する
かのようになる。
今日の課題1
• キーボードから数を入力として受け取り、その
個数分のint型データをキーボードから受け取
り、それらの最大値を画面に出力するプログ
ラムをcallocを用いて書け。
今日の課題2
受験者N人(Nは実行時にキーボードから入力)の氏名およ
び数学、英語の2科目の試験の点数をキーボードから入力
し、氏名、各科目の点数、合計点を一覧表にして表示した
い。これを行うプログラムを、callocを用いて書け。
各受験者の氏名と点数を入力する部分、合計点を計算す
る部分、一覧表示をする部分は、別々の関数として定義し、
それらをmain関数から呼び出す形でプログラムを記述せよ。
(実行例)
[sasano@oli001 11kai]$ ./a.out
人数を入力してください: 2
氏名: 芝浦太郎
数学: 90
英語: 90
氏名: 芝浦次郎
数学: 100
英語: 100
氏名
数学 英語 合計
芝浦太郎
90
90
180
芝浦次郎 100 100 200
構造体配列の動的確保(pointでの例)
(0) point構造体を定義
(1) point構造体へのポインタ型の変数pを宣言しておく。
point *p;
(2) Nにscanfで配列の要素数を入れる。
(3) p = (point *) calloc (N, sizeof (point)); で必要な領域を確保し、その先頭ア
ドレスをpに代入
(4) pを使って、確保した領域内の各要素にアクセス。
p[0], p[1] などが領域内の各構造体を表す。(*p, *(p + 1), 等でもよい)
p[0].x, p[0].y, p[1].x, …などが、領域の中に確保された各構造体のメン
バーを表すことになる。
p -> x, p -> y, (p+1)-> x 等、アローを使った表記でもよい。
typedef struct {
double x;
double y;
} point;
チャレンジ課題
• 課題2の表示を、合計点の高い順に行えるよ
うにせよ。