8-7 <発展> 汎用ポインタと可変長配列

この記事は約4分で読めます。

本トピックは発展的な内容を扱うので飛ばして構いません。一通りC言語の学習を終えてから見ることをお勧めします。

汎用ポインタ

これまで説明した通り、ポインタはメモリ上のオブジェクトを指すアドレスです。アドレスなので、intへのポインタであってもdoubleへのポインタであってもポインタ自体の大きさは同じです。ということは、変数や配列を指すポインタはすべて同じ型と考え、処理を共通化することができます。それが汎用ポインタです。

汎用ポインタはvoid*型として表します。void*型をint*型やdouble*型にキャストして使用します。

可変長配列

5-1では配列の要素数は定数でなくてはならないと書きましたが、状況によって要素数を変えながら定義したいときがあります。また、要素数をコンパイル時に決めないといけない場合は多めに要素を確保しなければならず、無駄が発生します。そんなときのために、メモリ領域を動的に確保するmalloc関数が使えます。

#include <stdlib.h>
void* malloc(size_t size);

この関数はsizeバイトのメモリ領域を確保し、その領域のアドレスを汎用ポインタとして返します。メモリの確保に失敗した場合はNULLが返ってきます。この引数は要素数とsizeof(型)の積を指定します。

配列の使用が終わったときはメモリを解放します。

#include <stdlib.h>
void free(void *p);

メモリの解放を忘れると、メモリの使用量が徐々に増加するメモリリークというバグの原因となります。

#include <stdio.h>
#include <stdlib.h>
int main(void){
	int *p;
	int n;
	int s=0;
	puts("1-1000までの整数を入力してください。");
	scanf("%d",&n);
	p=malloc(n*sizeof(int));
	if (p==NULL) return -1;
	while (s!=1){
		puts("メモリを解放しますか?\nYes...1 No...0");
		scanf("%d",&s);
	}
	free(p);
	return 0;
}

実行結果

1-1000までの整数を入力してください。
1000
メモリを解放しますか?
Yes...1 No...0
1

mallocはメモリ領域を確保するだけですが、確保と同時に0で埋めるにはcalloc関数を用います。

#include <stdlib.h>
void* calloc(size_t num,size_t size);

sizeバイトの領域をnum個確保し、0で埋めます。intなどの整数型は0で初期化することと同じ意味ですが、doubleやfloatは0で初期化されるとは限りません。

確保した領域の大きさを変えるには、realloc関数を用います。

#include <stdlib.h>
void* realloc(void *mem,size_t size);

ポインタmemが指す領域をsizeバイトだけ拡張します。そのあとアドレスを返します。アドレスが変わる場合があるので、戻り値を利用する必要があります。確保に失敗するとNULLが返ってきます。

この性質から、以下のようなコードを書くとpがNULLの時に元々のアドレスを失います。

p=realloc(p,要素数*sizeof(型));
if (p==NULL)
{
	return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main(void){
	int *p,*p_t;
	int n;
	int s=0;
	p=malloc(100*sizeof(int));
	if (p==NULL) return -1;
	while (s!=1){
		puts("メモリを解放しますか?\nYes...1 No...0");
		scanf("%d",&s);
	}
	p_t=realloc(p,100*sizeof(int));
	if (p_t==NULL){
		free(p);
		return 0;
	} else p=p_t;
	s=0;
	while (s!=1){
		puts("メモリを解放しますか?\nYes...1 No...0");
		scanf("%d",&s);
	}
	free(p);
	return 0;
}

実行結果

メモリを解放しますか?
Yes...1 No...0
1
メモリを解放しますか?
Yes...1 No...0
1

コメント