画像(logo)

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

広告

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

広告

↓2016年02月29日発売↓

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

目次へ戻る

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

今回はポインタまとめ[その1]として少しマトモ?な使い方を見ていきましょう!

グローバル変数のように使う

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

void func(int *);

int main(){
	int suji = 10;

	printf("関数「func」の前 = %d\n",suji);

	func(&suji);

	printf("関数「func」の後 = %d\n",suji);

	return 0;
}

void func(int *suji){
	*suji = 100;
}

■実行結果■

画像(ce_8_1)

今までは他の関数に何か変数を渡してその値を変更しても、元の「int main()」に戻れば変数の値も元に戻っておりました。

値をコピーしていた感じですね。

しかしアドレスを渡す事により元の変数の値も変更できるようになります。

まずは関数「func()」のプロトタイプ宣言を見てみましょう!

void func(int *);

アドレスを渡す時は「*」アスタリスクをつけてポインタ変数にします。

次に「int main()」内を見てみましょう。

func(&suji);

頭に「&」アンパサンドをつけて変数のアドレスを渡しております。

では関数「func」の内容を見てみましょう!

void func(int *suji){
	*suji = 100;
}

「*」アスタリスクをつける以外は通常の変数の場合と同じですね!

これでメモリアドレスの方から変数「suji」を直接いじれるので元の値も変更できるようになります。

グローバル変数のように使う(配列)

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

void func(int *);

int main(){
	int suji[3] = {1,3,5};
	int i;

	for(i=0;i<3;i++){
		printf("関数「func」前のsuji[%d] = %d\n",i,suji[i]);
	}

	func(suji);
	printf("\n");

	for(i=0;i<3;i++){
		printf("関数「func」後のsuji[%d] = %d\n",i,suji[i]);
	}

	return 0;
}

void func(int *suji){
	int i;
	for(i=0;i<3;i++){
		*(suji + i) = *(suji + i) + 1;
	}
}

■実行結果■

画像(ce_8_2)

今度は配列のアドレスを渡してみます!

まずはプロトタイプ宣言を見てみましょう!

void func(int *);

配列のアドレスになるので「int *[]」みたいになりそうな気がしますが、変わらず「*」アスタリスクを付けるだけです。

ちなみに「int *[]」と書くと違う意味になりますが文法的にはあってるのでエラーはでません。

ワケわからない結果になってしまうかもしれないので注意しましょう!

void func(int *suji)

関数「func」本体の部分も変わりありません。

続いて「int main()」内を見てみましょう!

関数「func」に配列のアドレスを渡します。

func(suji);

ちょっと思っていたのと違う書き方だったのではないでしょうか。

これまでさんざん「&」アンパサンドが〜と言ってきましたが配列の場合は配列名だけ書けばアドレスを表す事になるのです!

そういえば以前関数の事を学んだ時にも配列を他の関数に渡す時は

func(suji);

のように配列名だけを渡していましたね。

そうなんです!

あれも実はアドレスを渡していたんですね!

このへんで忘れかけた「printf()」とか「scanf()」の時に「&」を付けたりつけなかったり・・・、の意味もわかりますね!

それじゃあそのアドレスを受け取る関数の方の

void func(int suji[]){
	
}

これと

void func(int *suji){
	
}

この違いはなんだんだ?と思いますよね。

次回もう少し詳しくこのへんについても触れていく予定ですが、実はこの2つはほとんど同じ意味になるんですね!

このあたりも大変紛らわしいのですが、まあこれがC言語というものなのでガマンしましょう。

広告

アドレス自体を動かす

前回もやりましたが、アドレス自体を動かすのを今度は関数を通してやってみます。

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

void func(int *);

int main(){
	int suji[10] = {1,2,3,4,5,6,7,8,9,0};
	int i;

	printf("関数「func」前のsuji\n");
	for(i=0;i<10;i++){
		printf("%d ",suji[i]);
	}
	printf("\nsujiのアドレス = %x\n",&suji);

	func(suji);

	printf("\n");

	printf("関数「func」後のsuji\n");
	for(i=0;i<10;i++){
		printf("%d ",suji[i]);
	}
	printf("\nsujiのアドレス = %x\n",&suji);

	return 0;
}

void func(int *suji){
	while(*suji){
		(*suji)++;
		suji++;
	}
}

■実行結果■

画像(ce_8_3)

アドレス自体を動かしてその内容を確認、変更します。

関数を通しても「*suji」→内容、「suji」→アドレスなどの基本を理解していれば簡単ですね!

ただちょっと気になるトコロがありますね。

アドレスを渡したのにアドレスの値は変化しておりません。

これはポインタ習いはじめの時によく勘違いしそうな事なのですが、渡されたアドレスが持っている内容は直接変更する事ができますが、そのアドレス自体は通常の変数と同じように値を一時的にコピーしているような状態なのでさきほどのプログラムで言いますと関数「func()」が終わった時点でなくなってしまうという事です!

なんかアドレスを・・・、とか直接変更・・・、とか言われるとアドレスは絶対的なものみたいに勘違いしてしまいそうになりますよね。

このあたりがだいぶややこしいのですが、「そんなの当たり前じゃん!」と思う方は気にしないでください。

値渡しと参照渡し

今回は他の関数にアドレスを渡して、そのアドレスを通して値を確認したり変更したりという事をやってきました。

これを参照渡しと言います。

それに対してポインタを使わず普通に変数を渡すやり方を値渡しと言います。

参照渡しは今後C言語を学ぶ上でたくさん使われるとても大事な技術になりますので何回も練習して身につけましょう!

ポインターとして値を戻す

ポインター変数を引数として渡すやり方は学びました。

今度はポインター変数を戻り値として受け取ってみます。

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

int* func(int *);

int main(){
	int ary[5] = {1,2,3,4,5};
	int *ary_p = &ary[0];

	printf("func()前のary_pの内容 = %d\n",*ary_p);

	ary_p = func(ary_p);

	printf("func()後のary_pの内容 = %d\n",*ary_p);

	return 0;	
}

int* func(int *p){
	p++;
	return p;
}

■実行結果■

画像(ce_8_4)

ポインターにするには「*」アスタリスクでしたね!

int* func(int *);

戻り値の場合は「int」や「char」にアスタリスクがつきます。

ついでに「char」の場合も見てみましょう。

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

char* func(char *);

int main(){
	char bunsyou[128] = "aaaaa";
	char *bunsyou_p = &bunsyou[0];

	printf("関数func()前\n");
	printf("%s\n\n",bunsyou_p);

	bunsyou_p = func(bunsyou);

	printf("関数func()後\n");
	printf("%s\n\n",bunsyou_p);

	return 0;
}

char* func(char *p){
	int i;
	for(i=0;i<5;i++){
		*(p + i) = *(p + i) + 1;
	}
	p++;
	return p;
}

■実行結果■

画像(ce_8_5)

ここまでくればなんとなく気づいているかもしれませんが、配列の場合はもともと参照渡しなのでわざわざアドレスを戻す必要はないのですが(そのままでも元の値は変更されるので)ここはあえてやっております。

文字列のアドレスを渡して

bunsyou_p = func(bunsyou);

それぞれの要素を「+1」して

for(i=0;i<5;i++){
	*(p + i) = *(p + i) + 1;
}

さらにそのアドレスを一つ進めて戻す

p++;
return p;

という事をしております。

内容が変更されて、受け取ったポインタも一つ進められているのが確認できますね!

この2つのサンプルを見る限りまったく意味のない事をしているのでポインターの戻り値がどこで使われるのか想像しづらいと思いますが、もう少し高度なプログラムになるとちょこちょこ使われるので書き方だけ覚えておきましょう!

では次回はポインタを使う上での注意点とポインタのポインタ、いわゆるダブルポインタについてやっていきたいと思います。

次回

九日目 ポインタ6

□ページの先頭へ□

□目次へ戻る□

□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時点)