ポインタのポインタ

サンプルプログラム

普通の変数にポインタ変数があるように、実はポインタ変数にも、ポインタがあります。まずは、以下のサンプルを実行してみてください。

listex3-fp-1:main.c
#include <stdio.h>

void main(){
	char *s[3] = { "Taro","Hanako","Tom" };
	char **pps = NULL;	//	ポインタのポインタ
	int i;
	for(i = 0; i < 3; i++){
		printf("%s¥n",s[i]);
	}
	pps = s;
	for(i = 0; i < 3; i++){
		printf("%s\n",*pps);
		pps++;	//	アドレスを一つずらす
	}	
}
実行結果の例
Taro
Hanako
Tom
Taro
Hanako
Tom

4行目を実行することにより、s[0]には"Taro"、s[1]には"Hanako"、s[2]には"Tom"という値が入った文字列の変数(二次元配列)が出来上がります。7から8行目で、それらを表示しています。

5行目で定義されているのが、ポインタのポインタです。ポインタのポインタは、変数の型の跡に**をつけます。

10行目で、ppsに、sを代入しています。変数sは、それぞれの文字列の先頭アドレスのポインタの配列となっていることから、これによりs[0]のアドレスを取得していることになります。

型の定義の仕方こそ特殊ですが、関数ポインタも、ポインタ変数の一種です。ただ、そこに代入するのはあくまでも関数です。

引数として利用する場合のサンプル

続いて、関数の引数としてポインタのポインタを利用するケースを見て見ましょう。以下のサンプルを実行してみてください。

listex3-fp-2:main.c
#include <stdio.h>
#include <string.h>
#include <malloc.h>

int createMemory(char**,int);
void freeMemory(char**);

void main(){
	char* s = NULL;
	createMemory(&s,255);
	strcpy(s,"HelloWorld\n");
	printf("%s",s);
	freeMemory(&s);
}

//	メモリの確保
int createMemory(char** mem,int size)
{
	if(*mem == NULL){
		*mem = (char*)malloc(sizeof(char) * size);
		//	メモリが生成できなければエラー
		if(*mem == 0){
			return -1;
		}
		return 0;
	}
	//	与えられたメモリの値が不定ならば、エラー
	return -1;
}

//	メモリの開放
void freeMemory(char** mem)
{
	if(mem != 0){
		free(*mem);
	}
}
実行結果
HelloWorld

createMemory、および、freeMemoryという関数で、引数としてポインタのポインタが利用されていています。このサンプルのように、メモリの生成や消去を関数内で行いたい場合は、ポインタではなく、ポインタのポインタを渡すことになります。

この方法の利点は、ポインタの状態のチェックなどといったコードを関数内でできることから、ソースコードの中に同じようなコードを何度も記述する必要がないという点です。