画像(logo)

HOME/[C言語 入門]小学生でもわかるC言語2 目次/九日目 ポインタ6

広告

[C言語 入門 小学生でもわかるC言語2]
九日目 ポインタ6

広告

↓2016年02月29日発売↓

12歳からはじめる ゼロからのC言語 ゲームプログラミング教室

目次へ戻る

ポインタまとめ[その2]

今回はポインタまとめ[その2]として注意するトコロと後半にポインタのポインタ、ダブルポインタの使い方について見ていきたいと思います。

ポインタの方言

/*九日目 プログラム1*/
#include <stdio.h>

int main(){
	int suji = 100;
	int *p = &suji;
	int* p2 = &suji;

	printf("p = %x\n",p);
	printf("内容 = %d\n",*p);
	printf("p2 = %x\n",p2);
	printf("内容 = %d\n",*p2);

	return 0;
}

■実行結果■

画像(ce_9_1)

これはポインタの方言と言いますか、このように書いても同じようにポインタ変数として扱われます。

もちろん引数でも

void func(int *suji);
void func(int* suji);		

これも同じ意味で扱われます。

int* func(void);
int *func(void);		

戻り値においても同じような書き方ができるようです。

今度は文字配列の場合です。

/*九日目 プログラム2*/
#include <stdio.h>

int main(){
	char bunsyou[20] = "hello";
	char *p = bunsyou;
	char *p2 = &bunsyou[0];

	printf("pのアドレス = %x\n",p);
	printf("pの内容 = %s\n",p);
	printf("p2のアドレス = %x\n",p2);
	printf("p2の内容 = %s\n",p2);
	
	return 0;
}

■実行結果■

画像(ce_9_2)

配列の要素を省略した場合と、配列の先頭のアドレスだけを入れた場合ですね。

char *p = bunsyou;
char *p2 = &bunsyou[0];

結果を見るとこちらも同じような意味になります。

いろんな書き方

こんな感じで少しあげただけでも同じ意味なのにポインタはいろんな書き方ができるのです。

配列のアドレスとは?

ところで先ほどもあげた配列のアドレスについてですが、結果を見るとどちらも同じアドレスが入っております。

前回までは深くは考えずこの配列のアドレスを他の関数に渡したりしていたワケですが、この時渡しているのは何なのか?というのをもう少し詳しくみてみたいと思います。

それぞれのサイズを調べる「sizeof()」演算子というのを使います。

/*九日目 プログラム3*/
#include <stdio.h>

void func(int *);

int main(){
	char hairetu1[5] = "abcd";
	int hairetu2[5] = {1,2,3,4,5};
	int *p = hairetu2;

	printf("charのサイズ = %d\n",sizeof(char));
	printf("hairetu1 = %d\n",sizeof(hairetu1));
	printf("intのサイズ = %d\n",sizeof(int));
	printf("hairetu2 = %d\n",sizeof(hairetu2));

	printf("\n");

	func(p);	

	return 0;
}

void func(int *p){
	printf("関数func内「p」のサイズ = %d\n",sizeof(p));
}

■実行結果■

画像(ce_9_3)

前半に軽く使い方を示したプログラムを書きましたが

sizeof(char)

のように書けばそのサイズを調べる事ができます。

配列なども同じように調べる事が可能です。

結果を見ると「char」のサイズが「1」、「int」のサイズが「4」、2つの配列もそれぞれその要素の大きさ分の大きさになっている事が確認できるかと思います。

ここで問題の関数「func()」に渡した配列のポインタの大きさですが、結果を見ると・・・、

関数func内「p」のサイズ = 4

なんと配列を渡したはずなのにサイズは「int」一つ分の「4」しかありません。

これはどういう事かと言いますと、関数に渡していたのは「配列の大きさの何か」ではなく、単なるその先頭アドレスを渡していたという事なんですね!

つまりはもしこの渡された元の配列などの大きさを知りたい時は、この関数内から調べる方法がないので引数の一つなどとしてそのサイズを渡してあげなければいけないという事です。

広告

配列の文字列と文字列ポインタ

次もややこしい例です。

/*九日目 プログラム4*/
#include <stdio.h>

char* func(void);
char* func2(void);
char* func3(void);

int main(){
	printf("%s\n",func());
	printf("%s\n",func2());
	printf("%s\n",func3());

	return 0;
}

char* func(){
	return "hello world";
}

char* func2(){
	char bunsyou[20] = "hello world";
	return bunsyou;
}

char* func3(){
	char *bunsyou = "hello world";
	return bunsyou;
}

■実行結果■

画像(ce_9_4)

以前文字列ポインタを扱った時に文字列ポインタは配列の文字列と違って変更がきかないと言いましたが、もう少し注意すべき点があります。

あまりこのようなシチュエーションはないと思いますが、3通りの書き方で文字列を戻り値として返してみました。

ここで文字列とポインタの違いが垣間見えるのですが、基本的に自作関数の中で宣言した変数はその関数が終わった時点でなくなってしまいます。

「func2()」の例ですね。

途中から意味不明な文字に変わってますね。

これは関数「func2()」を抜けた時点で関数「func2()」内で宣言された配列はなくなってしまったという事を示しております。

ローカル変数ってやつです。

そうしないためにはグローバル変数にする、「int main()」で配列を宣言するなどの工夫が必要です。

しかし次の「func3()」の例を見てみましょう。

実行例を見てみると・・・、

ポインタで宣言した場合はそのポインタは残り続けるのです!

配列の文字列とポインタの文字列はほとんど同じに見えてあきらかな違いがあるという事ですね!

自分のポインタから

配列の要素数を省略するとその配列の先頭アドレスになると説明しました。

ではこのアドレスを使って直接他の要素を見る事はできないのでしょうか?

というのを試したプログラムになります。

/*九日目 プログラム5*/
#include <stdio.h>

int main(){
	int ary[10] = {1,2,3,4,5,6,7,8,9,0};
	int i;
	
	for(i=0;i<10;i++){
		printf("%d ",*(ary + i));
	}
	
	printf("\n");

	return 0;
}

■実行結果■

画像(ce_9_5)

思った通りポインタのようにその内容などを確認できましたね。

ではもしかして!と思って

ary++;

としてみると・・・、これはエラーになります。

配列名だけの場合はその先頭アドレス固定なのです。

ポインタ変数と通常の変数はやっぱり違うという事ですね!

ダブルポインタ

最後にポインタのポインタ、ダブルポインタについて使い方だけ軽く見てみましょう。

/*九日目 プログラム6*/
#include <stdio.h>

int main(){
	int suji = 120;
	int *p = &suji;
	int **p2 = &p;

	printf("変数sujiの内容 = %d\n\n",**p2);

	printf("変数sujiのアドレス = %x\n",&suji);
	printf("変数sujiのアドレス = %x\n",p);
	printf("変数sujiのアドレス = %x\n",*p2);

	printf("\n");

	printf("ポインタ変数pのアドレス = %x\n",&p);
	printf("ポインタ変数pのアドレス = %x\n",p2);

	return 0;
}

■実行結果■

画像(ce_9_6)

ポインタにも当たり前ですがアドレスがあります。

そのアドレスを保存するのがダブルポインタになります。

使い方はその言葉通り「*」アスタリスクを2つ付けます。

int *p;
int **p2 = &p;

これでポインタ変数pのアドレスが保存されます。

大元の内容、変数sujiの値を確認したい場合は

printf("変数sujiの内容 = %d\n\n",**p2);

のようにします。

ではその変数sujiのアドレスを確認したい場合は

printf("変数sujiのアドレス = %x\n",*p2);

のようにします。

そしてダブルポインタp2が保存しているアドレス、この場合はポインタ変数pのアドレスですね、これを確認したい場合は

printf("ポインタ変数pのアドレス = %x\n",p2);

のようにします。

基本的な使い方だけでもだいぶ頭が痛い話ですね!

でも大丈夫です!

よっぽど高度なプログラムにならないとまず使う事はないので。

その頃はポインタなんて余裕!ってなってると思いますので今のトコロはさわりだけで良いと思われます。

もううんざりだっ!

同じ意味なのにたくさん書き方があったり、ほとんど同じ意味なのにエライ細かい部分で違いがあったり、果てはダブルポインタとか・・・。

もう、うんざりしますよね。

まあこのへんを完璧にしようとするとたぶんポインタを学ぶ間に人生が終わってしまうので、ここまで言っておいてなんですが、最初は前回ポインタ5で触れたような内容がおそらくよく使われるようなカタチなのでこのへんから少しずつ身につけていけばいいと思います。

では気分一新、次回はようやくポインタから離れてセットで一つのものを扱う構造体について見ていきたいと思います。

次回

十日目 構造体(こうぞうたい)

□ページの先頭へ□

□目次へ戻る□

□HOME□

広告

↓2017年06月16日発売↓

やさしいC 第5版 (「やさしい」シリーズ)

新品価格
¥2,700から
(2017/5/1 13:05時点)

↓2014年08月09日発売↓

新・明解C言語 入門編 (明解シリーズ)

新品価格
¥2,484から
(2017/5/1 13:08時点)

↓2016年02月20日発売↓

新・解きながら学ぶC言語

新品価格
¥2,160から
(2017/5/1 13:10時点)

↓2017年02月11日発売↓

C言語プログラミング基本例題88 88

新品価格
¥3,024から
(2017/5/1 13:12時点)

↓2016年12月15日発売↓

Cの絵本 第2版 C言語が好きになる新しい9つの扉

新品価格
¥1,490から
(2017/5/1 13:13時点)

↓2017年02月08日発売↓

新・明解C言語で学ぶアルゴリズムとデータ構造 (明解シリーズ)

新品価格
¥2,700から
(2017/5/1 13:15時点)