❗️ 注意:この記事が作成されてから既に
日が経過しています。情報の鮮度にご注意ください
🖥 説明:この記事は、かつてC言語を学んでいた頃に書いたブログ記事で、古いWordPressブログから復元したものです。
はじめに
C言語を学び始めたばかりの初心者は、ポインタについてよく困惑します。以下に、私がポインタについて理解していることを説明します。
まずいくつかの問題を明確に
変数の本質
変数の本質は、あるメモリアドレスを指し示す名前表現です。コンパイル後のオブジェクトコードには変数名は存在せず、コンパイラはコンパイル段階で変数名とそれが表すメモリアドレスのマッピングテーブルを作成し、変数の型/名前/アドレスを記録します。
変数を宣言するとき、実際にはOSに対してメモリアドレスの領域を要求しています。値を代入するときは、そのアドレスに該当するデータを格納しているのです。
変数代入の本質
変数xを宣言し、アドレスが0x0001で、そのアドレスに1が格納されているとします。xに2を代入する場合、以下の手順が実行されます:
- マッピングテーブルから変数xのアドレスを検索し、0x0001を取得
- 値2をそのアドレスに格納、代入完了
ポインタの代入も同様です。
*の3つの意味
- 関数プロトタイプを宣言するとき、例えば:この場合の*aは、fn関数がポインタ変数(つまりメモリアドレス)をパラメータとして受け取ることを示し、そのパラメータがaです。
1
void fn(int *a) - 関数本体で、*aが等号の左側に現れる場合、ポインタaが示すメモリアドレスに等号右側の値を格納することを意味します。例えば:
1
2
3void fn(int *a) {
*a = 3;
} - 関数本体で、*aが等号の右側に現れる場合、ポインタaが示すメモリアドレスから値を取り出すことを意味します
1
2
3void fn(int *a) {
int tmp = *a;
}
例: 2つの変数の値を交換する関数
1 | |
例の説明:
- main関数で2つの変数x,yを宣言。アドレスは&xと&yで、それぞれ値1と2が格納されています。
- swap関数が呼び出されると:
- 2つのポインタをパラメータとして宣言、int *aやint *bは仮引数で、int型の値を指すポインタであることを示します。つまりこの関数はint型値へのアドレスを2つパラメータとして期待しています。この時点で、*はポインタ変数を受け取ることを表します。
- swap関数が実行されると:
- 仮引数aに&x(つまりxのメモリアドレス)が代入され、ローカル変数aのメモリアドレスに格納されている値はxのメモリアドレスになります。同様に、仮引数bには&y(yのメモリアドレス)が代入されます(*を付けた仮引数は単にそのパラメータがポインタ型であることを示しているだけです)
- ローカル整数変数tmpを宣言すると、*aは:
- ステップ1: メモリ内のaのアドレスを特定
- ステップ2: aのアドレスに格納されている値を読み取る(この値もメモリアドレスで、変数xのアドレス)
- ステップ3: aの値の値(つまりaの値であるアドレスの先の値、xの値である1)を返す
- _bは変数bの値(メモリアドレス)の値(そのアドレスに格納されている値)を_記号で取り出すことを表し、つまりアドレス&yに対応する値である2を、*a(アドレス&xに格納されている値、a=&xなのでxの値である1)に代入します。swap関数内でmain関数の変数xのアドレスに格納されている値が変更されたことに注意
- 最後の文では、tmpは値1であり、それを*b(つまりbのアドレスに格納されている値、2)に代入し、その値を1で上書きします。ここでswap関数はポインタを通じてmain関数のyのアドレスに格納されている値も変更しています
理解を深めるための図:


- EOF -
この記事の初出:
C言語におけるポインタの正しい理解 - Xheldon Blog
人生の重要な選択に直面したとき、最善の方法を誰かが教えてくれて、貴重な時間を無駄にせずに済めばと、私はよく願っています。だからこそ、自分の経験を踏まえて頻繁にブログを書き、広大なインターネットのこの小さな片隅に、私にとって一度きりの人生経験を記録し、助けを求める人々の力になれればと思っています。