第十回-03 アドレスとポインタ (2)

引続き、ポインタに関する学習を進める。

ポインタについて学ぶ際、重要なのは「初期値を定める」ことである。

初期化すること、と言っても良いのだが、C++ では「初期化」というのは文法上厳密な意味を持つ用語であるので、
ここでは「初期値を定めること」と記述することにする。


関数内の変数は初期値を定めることが必要

さて、ポインタに限らず、関数内で宣言される変数 (自動変数と呼ばれる) は、
宣言しただけでは値は定まらないことをここでは知って欲しい。

例えば、以下のように、x を宣言だけして値を表示しようとすると…



(Visutal Studio の Debug ビルドの場合)、以下のように「x が初期値が定まっていないのに使われているよ」とエラーが出る。



ポインタ変数でも同様であり、以下のようにポインタ変数 p の指す先を定めずに p の指す変数の値を変更使用とすると…



やはりエラーが出る。



ここまで学習を進めた皆さんならば、これらの理由は比較的容易に理解できるはずである。
下図を見てみよう。



上図 (a) は以下の宣言をした直後のメモリの模式図である。
メモリとはOSによってアプリケーションに割り当てられるのであるが、
そのメモリ領域が OS が起動してからどのように用いられたかはアプリケーション側からはわからない。

つまり、x の値や、p に格納されているアドレス (つまり p の矢印が指す先) はこの時点では全く予想できない

int x;
int *p;


ここで、下の命令を実行すると、上図 (b) のような状態になる。
「p=0;」は新たに登場した記法であるが、これはポインタに 0 を代入しているが、
この 0 のことをヌルポインタと言い、どこも指さないことを保証しているポインタである。

(ちなみにヌルポインタを 0 と書くのは C++ からの記法であり、C 言語時代は p = NULL; の様に記述していた。 Java や C# では null と書く)

x = 5;
p = 0;   // ポインタに 0 を代入しているが、これをヌルポインタという


ただし、ポインタ p に 0 を代入しても、それは「メモリ上で見当違いな所を指していない」ことを保証するだけで、
まだ使うことはできない。実際には、実体を持つ変数を指すことで、初めて利用することができる (上図 (c))。


swap 機能をポインタで実現

さて、ポインタについて学んでいるわけだが、
実際にポインタの使い方をマスターするにはtrial and error を何度も繰り返すしかない。

しかし、今日のところは最低限 ことを理解して欲しい。

その上で、先週の課題である swap 関数を考えよう。
実際にどう記述するかはともかく、下図のように、 swap 関数内の x、y が main 関数の x、y を指すポインタであれば
swap 関数内から main 関数内の変数を操作できることが想像できるだろうか。



それを実現するのが以下のプログラムである。 前回課題次からの変更点を太字にしたのでその点を変更して動作を確認して欲しい。

#include <iostream>

void swap(int *x, int *y); // 関数のプロトタイプ。swap 関数のx、yを ポインタとして宣言

int main(int argc, char* argv[])
{
	int x = 10;
	int y = 20;

	std::cout << "[交換前]\n";
	std::cout << "x=" << x << ", y=" << y << "\n";

	swap(&x, &y);  // 関数による交換操作。main 関数内の変数 x、y のアドレスを渡している

	std::cout << "[交換後]\n";
	std::cout << "x=" << x << ", y=" << y << "\n";

	return 0;
}

void swap(int *x, int *y)  // swap 関数の定義。引数は上にある関数のプロトタイプに合わせる。
{
	int tmp;  // tmp は通常の変数として定義
	tmp = *x; // x、y の値はポインタ変数の値としてアクセス
	*x = *y;
	*y = tmp;
}


ポイントは、 なお、この例は、main 関数における x、y は通常の変数であり、swap 関数における x、y はポインタ変数になっており、
同じ変数なので慣れないと混乱するかも知れない。

変数名を分けたいという人は

void swap(int *px, int *py);  // swap 関数のプロトタイプ。関数の定義をどう変更するかは省略。各自試してみると良い。


のように、swap 内のポインタ変数の名前を変えてみるのも良いだろう。
プロトタイプの変更のみしか記述しなかったので、swap 関数の定義部で どう変更すべきかは各自試してみると良い。



←第十回-02 アドレスとポインタ (1)第十回課題→

非情報系学生のための C/C++ 入門に戻る