C言語にはポインタという機能があり、C++も同様に使えたような記憶があります。
使い方を確認してみたいと思います。
普通の変数の値をコンソールに出力
#include <iostream>
int main() {
int i = 5;
std::cout << i << std::endl;
return 0;
}
/*
結果
5
*/
int i = 5
int型の変数iを宣言し5で初期化(変数へ代入される)しています。
std::cout << i << std::endl;
標準出力にiの値を出力します。結果5が出力されます。
ポインタ変数を使い値をコンソールに出力
#include <iostream>
int main() {
int i = 5;
int* p;
p = &i;
std::cout << *p << std::endl;
return 0;
}
/*
結果
5
*/
int* p;
int*はint型のポインタ変数pを宣言しています。
ポインタはメインメモリーのアドレスを表し、変数型の後に*(アスタリスク)をつけることで変数型のポインタを保持する変数であることを表します。
通常の変数からポインタを取得する方法として変数の前に&(アンバーサンド)をつけます。
p = &i;
変数の宣言と初期化がセットになっていないと気持ち悪いので一行にまとめると次の様になります。
int* p = &i;
ポインタ変数から値を取得する場合、ポインタ変数の前に*(アスタリスク)をつけます。
std::cout << *p << std::endl;
ポインタの使いどころ
関数の引数によく使われます。理由として引数として渡すデータ量がポインタ変数の大きさ分ですみます。
巨大な配列や複雑なクラスのオブジェクトだったとしても、int型の数値だったとしてもポインタ変数のサイズは概ね同じになります。
ポインタを関数の引数とした場合、他所で定義した変数を自由に書き換えてしまえるので、危険をはらみますが使い方さえ誤らなければパワフルな機能だと言えます。
ポインタがメモリのアドレスだとすると、メモリ上にある変数だけではなく関数のアドレスを取得することも出来ます。他言語のデリゲートのような使い方が出来そうです。(関数ポインタもデリゲートもあまり使ったことが無いので誤りかもしれません)
感想
C++としていますが今回の記事の内容はC言語の知識を思い出して書いています。
現代のC++では今回扱ったポインタを安全に扱うための仕掛けが用意されているようなので、そちらはそのうち勉強したいと思います。
感想2
個人的のアセンブリ言語(マシン語)と他のプログラミング言語の違いは、変数の有無だと思います。
コンピュータはメモリからCPUのレジスタにデータを取得し、レジスタで演算処理を行い、結果をメモリに返すような処理を行います。メモリはアドレス(番地)を指定するのですが、1つのコンピュータで複数のプログラムが実行する環境では、アドレスを絶対値で指定する方法では問題が生じます。他のプログラムでメモリを使っている可能性があるので、コードを書いている時点ではアドレスを決め打ちすることが出来ません。ユーザープログラム以外でメモリを管理する機能があるとして、ユーザープログラムでは空きメモリの番地を取得し、そこから相対的な番地を計算し指定する必要があります。
絶対にしても相対にしても数値(アドレス)でデータの場所を管理するのは困難ですので、そこに任意の名前を付けた識別用のラベルを付けたと思われます。そのラベルに対して保存するデータが数値なのか文字なのか、型の情報を加えたものが、C言語の変数だと思われます。
こちらの変数は、人間が管理しやすい反面、メモリの実態にたどりつくために処理を挟むため(アドレス計算とか)、直接メモリを参照するよりパフォーマンスが悪いです。そこで出てくるのがポインタに成ります。アドレスはポインタ変数で隠蔽して扱いやすくしながら、メモリを直接参照することにより高いパフォーマンスが得られます。
メリットがパフォーマンスである点、近年のコンピュータのマシンパワーで相殺することで、態々ポインタを使わなくとも、多くのプログラムで実用的なパフォーマンスが発揮できるようになったので、比較的最近のプログラミング言語ではポインタ機能が無いのだと思われます。
コメント