【初心者向け】情報学部生と学ぶC言語 [6限目:ポインタ]

ポインタはC言語の学習における最難関のひとつとされています。現にポインタを理解出来ないためにC言語の習得を諦める人も多数います。そこでポインタを理解するために以下のステップを踏んで説明していきます。いずれもポインタを理解するためには必要な知識となるためひとつひとつしっかり理解されることを推奨します。

ふたつの値の和、差、積、商を求める関数を用いて、main関数内でそれらの答えを出力するプログラムを作りましょう。

#include <stdio.h>

void cal(int num1, int num2, int sum, int diff, int prod, double quot)
{
	sum = num1 + num2;
	diff = num1 - num2;
	prod = num1 * num2;
	if(num2==0)
		quot = 0;
	else
		quot = (double)num1 / (double)num2;
}

int main(void)
{
	int x,y;
	int wa = 0, sa = 0, seki = 0;
	double sho = 0.0;
	
	printf("整数1:");
	scanf("%d", &x);
	printf("整数2:");
	scanf("%d", &y);
	
	cal(x, y, wa, sa, seki, sho);
	
	printf("和:%d\n", wa);
	printf("差:%d\n", sa);
	printf("積:%d\n", seki);
	printf("商:%.1f\n", sho);
	
	return 0;
}
■実行結果[例]
整数1:30
整数2:8
和:0
差:0
積:0
商:0.0
あれれ~?おかしいぞ~?
答えが全部0になってるよ。

さすがコ○ン君!実は、こうなってしまうという所に「値渡しとは何なのか」という事が表されています。

値渡しとは

値渡しとは、コンピュータプログラム中で関数やプロシージャなどに渡す引数として変数などを指定する際、その値のみを引き渡す方式。渡された引数の値を変更しても呼び出し元の変数の値は更新されない。


引用元:IT用語辞典 e-Words

「渡された引数の値を変更しても呼び出し元の変数の値は更新されない。」という所に注目しましょう。

例えば上記の引用文を利用してこのページの中で以下のような文章に書き換えたとします。

値渡しとは、「アタイ・ワタシ」の2語から構成され、女の子の一人称を表す隠語である。

この時、このページ内の「値渡しとは」の文章(コピーした値)は変更されましたが、引用元の文章(オリジナルの値)は変更されませんよね?

これが値渡しというものです。値渡しでは引数によりひとつの値を計算して返すことは出来ますが、上記のプログラムのように複数の値を一度に計算したい時には、2個以上の値を返せないという性質上、実現できません。

そこで利用されるのがポインタです。

ポインタを理解するために、まずアドレスというものを理解しなければなりません。

アドレスとは

英語でアドレス(address)というと、住所や番地のほかに演説や講演といったような意味がありますが、ここで使うアドレスは番地ということを意味します。つまり、自分のいる所(=場所)を示しています。各々の変数には宣言した時にアドレスが付与されます。アドレスを用いる際、変数の前に”&”を用います。この”&”アドレス演算子と言います。そしてアドレスを出力するための変換指定には%pを用います。

pはpointerの意

変数名 アドレス演算子 変換指定
n &n %p

では、さまざまな型のアドレスを出力するプログラムを作りましょう。

#include <stdio.h>
int main(void){
	int a;
	double b;
	char c;
	
	printf("aのアドレス:%p\n", &a);
	printf("bのアドレス:%p\n", &b);
	printf("cのアドレス:%p\n", &c);
	
	return 0;
}
■実行結果[例]
aのアドレス:200
bのアドレス:204
cのアドレス:208

ここでは説明のためアドレスの出力を簡易的に”200″といったような10進数で表示していますが、通常は16進数で表示されると思います。[例]0x000000000000(実行環境により異なる)

ポインタとは(参照渡しとは)

最初に作成した「ふたつの値の和、差、積、商を求める関数を用いて、main関数内でそれらの答えを出力するプログラム」はポインタというものを使うことにより実現できます。以下のソースコードからポインタの使い方についてみていきましょう。

#include <stdio.h>

void cal(int num1, int num2, int *sum, int *diff, int *prod, double *quot)
{
	*sum = num1 + num2;
	*diff = num1 - num2;
	*prod = num1 * num2;
	if(num2==0)
	*quot = 0;
	else
	*quot = (double)num1 / (double)num2;
}

int main(void)
{
	int x,y;
	int wa = 0, sa = 0, seki = 0;
	double sho = 0.0;
	
	printf("整数1:");
	scanf("%d", &x);
	printf("整数2:");
	scanf("%d", &y);
	
	cal(x, y, &wa, &sa, &seki, &sho); // (num1 = x, num2 = y, int *sum = &wa, 以下略)を意味する
	
	printf("和:%d\n", wa);
	printf("差:%d\n", sa);
	printf("積:%d\n", seki);
	printf("商:%.1f\n", sho);
	
	return 0;
}
■実行結果[例]
整数1:20
整数2:8
和:28
差:12
積:160
商:2.5

解説

ポインタは変数の前に”*”を付けて宣言します。例えば「intへのポインタ型p」を生成したい時は以下のように宣言します。

int *p;

この時、変数は「『int型の値を格納する変数のアドレス』を格納する箱」を意味します。

そして

int *p;
p = &n;

とした時、ポインタpにはnのアドレスが格納されます。このことを「pはnを指す」といいます。

そして、ポインタが指し示す先の中身を使用する時には”*”(間接演算子という)を付けます。この時ポインタは格納されているアドレスを参照して、そのアドレスにある変数の中身を指します。そしてその変数の中身を出力します。このことを慣例的に参照渡しといいます。

ここで、ポインタの使い方のおさらいをしておきましょう。

int *p; // intへのポインタ型の変数pの宣言
p = &n; // pにnのアドレスを格納
printf(“%d”, *p); //間接演算子”*”をつける事で参照したアドレス先の中身を扱える

値渡しと参照渡しの違い

先程のプログラムからポインタを使わない場合(=値渡し)とポインタを使う場合(=参照渡し)の違いをみていきましょう。

値渡し

値渡しは中身が同じである変数を複製します。この時、格納されているアドレスが異なるため複製した値を変更してもオリジナルの値は変わりません。

参照渡し

参照渡しはオリジナルの値が格納されている変数のアドレスを複製します。オリジナルのアドレスが格納された変数の値を変更する時、そのアドレスを参照してオリジナルの値そのものを変更します。

そういえば、今まで何も考えずに使ってたけど
scanf(“%d”, &n);
ってアドレス演算子を使ってたんだ!

いい所に気づきましたね!scanfは「渡されたアドレスを参照して、そこにある変数の中身を入力される値に変更する」という関数だったのです。

ポインタについて理解できるかどうかがC言語について理解しているかどうかに直結するといっても過言ではありません。しっかり復習してポインタを理解できるようになりましょう。


C言語学習におすすめのテキストはこちら

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です