この記事で分かること
- ポインタとは何か?その概念について
- ポインタがコンパイラによって、どのように実装されているか
- ポインタのメリット・デメリットについて
この記事では、C言語のポインタについて入門者がざっくり理解できるように解説をしています。
C言語を学び始めたばかりの人にとって、ポインタは最初のハードルになるもので、理解するのが非常に難しい概念ですよね?
しかし、初心者から中級者になるに当たって、ポインタを正確に理解していることが、後々の自分の成長に響いてきます。
ポインタの概念や、メモリ上での実装イメージを持っていることが、今後必ず役に立ちますので、まずはこれらの内容をざっくりと理解して行きましょう!
次の章より、
- ポインタの概念の理解
- ポインタの実装の理解
- ポインタのメリット・デメリットの理解
- ポインタと配列の比較の理解
の順で、手書きの図を用いて解説していますので、まずは気軽に読み進めてみて下さい。
もし分からないことや質問などがあれば、ぜひコメント欄を活用してください!
ポインタの概念
最初に、ポインタの概念的な解説をしていきます。
とりあえず何に使うかは置いておいて、まずは一言で「ポインタとは何か?」を表してみます。
ココがポイント
ポインタとは、ある変数を指す示すものである
Pointer は元々英語で「指し示す物」という意味がありますから(英辞郎WEB)、まさにそのままの意味です。
ただし、C言語においては、変数 を 指し示すものに限定されます。
ポインタに他の意味はありません。これだけを理解すれば大丈夫です。
ポインタの概念を図で解説
人に説明する時に手書きに勝るものは無いと思うので、手書きの図を使って解説します。
以下の図は、C言語のポインタの概念イメージです。(後ほど説明します)
ポインタはある変数を指し示す矢印である
概念レベルでは、このように理解しておけば問題ありません。
ポイント
- ポインタは矢印の様なものをイメージします
- ポインタはある一つの変数を指し示しています
- ポインタは別の変数を指すように変えることが出来ます
- ポインタは何も指してない状態にすることも出来ます
ポインタの実装
C言語を深く理解するためには、ポインタがどのように実装されているかイメージ出来ることが大切です。
特に、メモリ上でどのように表現されているかをある程度理解していることが重要になってきます。
先ほどの概念理解では、ポインタを矢印の様なものであると述べましたが、ではその矢印をどのように実装しているかというと、実はポインタ自体も一つの変数になっています。
混乱してきましたか?
今回も手書きの図を使って、ポインタの実装イメージを解説します。
ポインタの実装を図で解説
以下の図は、C言語のポインタの実装イメージです。(後ほど説明します)
ポインタpは、100番地 に格納されている変数です。
変数aは、1000番地 に格納されている変数で、中身は整数の 1 です。
変数bは、1004番地 に格納されている変数で、中身は整数の 2 です。
変数cは、1008番地 に格納されている変数で、中身は整数の 3 です。
変数dは、100C番地 に格納されている変数で、中身は整数の 4 です。
ポインタpの中身はメモリのアドレスが格納されていて、その値は 1000 (番地) です。
メモリのアドレスを持つことで、矢印が指し示す先を表しています
ポイント
- ポインタもひとつの箱(変数)です
- ポインタの箱の中にはメモリのアドレスが入っています
- メモリのアドレスは指し示す先の箱を表しています
- ポインタは指し示す先の箱の大きさを知っています
ポインタのメリット
- 変数を丸ごとコピーしなくてもよいため効率が良い
- 指し示す先の値を直接変更出来るため、変数のコピーで操作するより処理が簡単になる場合がある
- 関数に配列を渡すことが出来るようになる
ポインタ変数はメモリのアドレスさえ格納出来れば良いので、サイズが非常に小さくて済みます。
(4バイト程度から大きくても8バイトあればアドレスを表現出来ます)
サイズの大きな変数や、構造体をコピーするのに比べて処理速度の面でも、メモリ使用量の面でも効率が良いです。
C言語は、プロセッサの性能やメモリ容量が潤沢でない場合が多い環境で使われることが多いため、ポインタを使うことにはメリットがあります。
ポインタのデメリット
- 指し示す先の変数の大きさを超えて書き込むことが出来るため、簡単にメモリ破壊が起きてしまう。
C言語が、安全ではないアンセーフなプログラミング言語と言われる理由はポインタにあります。
使い方を間違えると簡単にメモリを破壊してしまいますし、プログラムが動かなくなるだけなら良いのですが、悪意のあるコードでメモリを破壊されると、攻撃者による任意のコードを実行される恐れさえあります。
そのため、セキュリティ的な脆弱性を入れ込みやすく、メモリ破壊には細心の注意を払ってプログラムを書く必要があります。
メモリの破壊はコンパイラで検知できないこともあり、ポインタの動作を十分に理解してプログラムすることが重要になります。
配列との比較
ポインタと配列はほとんど同じものなので、構文が異なるだけの 糖衣構文 と見なすことが出来ます。
ポインタも配列も、ある変数の先頭のアドレスを持っていることに変わりはありません。
コンパイラの制限により出来ることに若干の違いはあるのですが、特に初心者のうちは同じものと見なしても問題ありません。
配列をポインタとして表現する例をサンプルコードでお見せします。
#include <stdio.h>
void pointer_array() {
int array[5]; // 配列
int* p = array; // ポインタ
array[0] = 0;
array[1] = 1;
array[2] = 2;
array[3] = 3;
array[4] = 4;
for (int i = 0; i < 5; i++) {
printf("ARRAY:%d / POINTER:%d \n", array[i], *(p + i));
}
*(p + 0) = 5;
*(p + 1) = 6;
*(p + 2) = 7;
*(p + 3) = 8;
*(p + 4) = 9;
for (int i = 0; i < 5; i++) {
printf("ARRAY:%d / POINTER:%d \n", array[i], *(p + i));
}
}
出力結果
ARRAY:0 / POINTER:0
ARRAY:1 / POINTER:1
ARRAY:2 / POINTER:2
ARRAY:3 / POINTER:3
ARRAY:4 / POINTER:4
ARRAY:5 / POINTER:5
ARRAY:6 / POINTER:6
ARRAY:7 / POINTER:7
ARRAY:8 / POINTER:8
ARRAY:9 / POINTER:9
この例では、ポインタと配列は同じものを指しているため、変数の値を共有しています。
まとめ
最後に、この記事の内容をまとめます。
要点
- ポインタはある変数を指し示す矢印である
- ポインタはメモリのアドレスを持つ変数である
- ポインタの一番のメリットは処理速度が上がること
C言語を始めたばかりの人にとって、ポインタの理解は最初のハードルになると思います。
逆に言えば、このハードルをクリアすれば他に難しい部分はほとんどありません。
関数ポインタやダブルポインタ(ポインタのポインタ)など、更に難しい内容もあると言えばありますが、これらはざっくり理解している程度でも実用上は問題ありません。
重要なのは、普通のシングルポインタについてしっかりと理解しておくことです。あとは応用で何とかなります。
この記事を読んで少しでも理解出来たら、ぜひ実際にプログラムを動かしてポインタの動作を色々と試してみて下さい!
きっと理解度が上がっているはずです。
それではまた、他の記事でお会いしましょう!