【C言語/C++】ポインタや配列の基礎知識「アドレス」とは?わかりやすく解説!
- アドレスとは何かを知りたい方!
- C言語/C++のポインタや配列の概念がよくわからない方!
- 実際に変数がどのようにメモリ領域をとっているのか知りたい方!
今回は、「アドレス」の解説になります。
C言語学習者が躓くランキング上位の「ポインタ」を理解するために欠かせない概念です。
ポインタを理解するためにも、基礎知識となる「アドレス」の概念を理解していきましょう。
それでは解説していきます!
アドレスとは?メモリの番地?
アドレスとは、「番地」を意味します。
C言語やC++などで使われる「アドレス」という単語は、「メモリアドレス」「メモリ番地」と同じ意味でつかわれます。
これ以降「メモリアドレス」として話を進めます。
結論からいえば、「メモリアドレス」とは、ひとメモリごとにつけられている通し番号のようなものです。
プログラムや変数は全てメモリに記録される?
本題に入る前に、少しパソコンの内部の話をしておきましょう。
パソコンには大きく分けて、以下のような部品があります。
パソコンの部品 | 主な役割 |
---|---|
中央演算処理装置 (CPU) | パソコンの脳の部分。CPUはあらゆるパーツを自在に操りパソコンを動かしています。 |
電源ユニット | パソコンの心臓部分。安定した電力を供給します。 |
主記憶装置 「メモリ」 | 補助記憶装置よりも高速でデータの読み書きが行える。CPUがメインで使う作業スペースとなります。 |
補助記憶装置 (HDD/SSD) | 主記憶装置と違い、電源を落としてもデータが消えない。比較的大容量。 |
その中でも、「メモリ」はCPUが使う作業スペースのようなものです。
何らかのプログラムを実行するときは、このメモリにプログラムを読み込んで、それを読み書きしながら実行します。
当然、変数もこのメモリに記録されます。
つまり、変数はメモリのどこかに格納されているということになります。
そりゃどこかに値を記録しておかなければデータが消えてしまいますからね。
では、メモリの「どの位置にデータが格納されているのか」を知れたら便利ではないでしょうか。(というより、わからなくてはいろいろ困るんですけどね。)
例えば、
「この変数はこのメモリ番地を使ってまーす。」
というのがわかれば、
外部(呼び出した関数など)からでも直接変数の値を変えることができるようになります。
大前提として、コンピュータがプログラムを実行していくとき、そのプログラムや変数などがメモリ上に読み込まれているというのを覚えておいてください。
このイメージを持てているかいないかでは、C言語系のプログラミングの理解度も変わってくるはずです。
int型の変数がメモリ内に格納されるイメージを理解する
例えば、C/C++で以下のように変数を定義したとしましょう。
int main(void)
{
// 変数aを初期化
int a = 1;
return 0;
}
そうすると、プログラムが動いたときのイメージは以下のようになります。
1行1行が1byteのデータと考えてください。
メモリアドレス | メモリを使用しているもの |
---|---|
0x000AB003 | ??? |
0x000AB004 | int a 使用中 |
0x000AB005 | int a 使用中 |
0x000AB006 | int a 使用中 |
0x000AB007 | int a 使用中 |
0x000AB008 | ??? |
今回の例では、0x000AB004 ~ 0x000AB007 までの4byteをint aが使用しています。
もう少し詳しく言うと、以下のように4byte = 32bitを管理しています。
- 0x000AB004 1~8bit目
- 0x000AB005 9~16bit目
- 0x000AB006 17~24bit目
- 0x000AB007 25~32bit目
1つのメモリアドレスにつき8bit = 1byteの表現が可能で、int型は4byteのデータ型なのでこのようにメモリを占有していることになります。
具体的な値を入れてみるとこうなります。(値がひっくり返ったりするので難しい)
メモリアドレス | メモリ内のデータ(0bは2進数表現という意味) |
---|---|
0x000AB003 | 0b ???????? |
0x000AB004 | 0b 01000000 |
0x000AB005 | 0b 00000000 |
0x000AB006 | 0b 00000000 |
0x000AB007 | 0b 00000000 |
0x000AB008 | 0b ???????? |
4byte区切りで表すと以下のようなります。(少し難しい)
メモリアドレス | メモリ内のデータ(0bは2進数表現という意味) |
---|---|
0x000AB000 | 0b ???????? ???????? ???????? ???????? |
0x000AB004 | 0b 00000000 00000000 00000000 00000001 |
0x000AB008 | 0b ???????? ???????? ???????? ???????? |
直観だと2bitごとに確保されてひっくり返ってるのでわかりにくいですが、コンピュータの内部ではこうなっています。(その辺は正直わからくても問題ない)
どうでしょうか。
「int型の変数が実際にどのようにメモリ内に確保されるか」を想像できたでしょうか?
詳しくはあまり触れないですが、この感覚はC言語系のプログラミングではとても重要になってきます。
変数のメモリアドレスを確認する「アドレス演算子 &」
int型の変数がメモリの4byteを用いて表現されていることをさらっと解説しました。
そのことは今後データ型の解説で詳しくするとして、今回はそのメモリアドレスが実際にどんな値なのかを見ていきましょう。
データのアドレスを確認するには「&」を使います。(読み方はアンパサンドorアンド)
これは、「アドレス演算子」と呼ばれ、データの前につけることで、そのデータが使用しているメモリの先頭アドレスの値に変換することができます。
先ほどの例で、int型の変数aを用いれば、「&a」とすることで先頭アドレス0x000AB004が得られます。
メモリアドレス | メモリを使用しているもの |
---|---|
0x000AB003 | ??? |
&a == 0x000AB004 (先頭アドレス) | int a 使用中 |
0x000AB005 | int a 使用中 |
0x000AB006 | int a 使用中 |
0x000AB007 | int a 使用中 |
0x000AB008 | ??? |
注意が必要なのが、何バイト使っていようが返ってくるのは先頭アドレスのみということです。
サンプルコードで確認する前に、アドレス演算子についてまとめておきましょう。
- 変数などの前につけて使用する。(例:&a)
- 変数などが使用しているメモリ領域の先頭アドレスを返す。
- 「&」の読み方は”アンパサンド”、または”アンド”。
以上のことを踏まえて、サンプルコードを見ていきましょう。
実行結果は以下のようになりました。
a:1
&a:00B5FD5C
int型の変数aはメモリアドレス00B5FD5Cからの4byteを使っているんだなということがわかります。
がしかし!このメモリアドレスの値は実行するたびに代わります。
つまり、このメモリアドレスは実行時までわからないのです。
また、実行環境によっては、メモリアドレスの桁数が増えたりもします。
↓以下の環境では16進数12桁になっています。
何回か試してみてメモリアドレスが動的に変わることを確認してみてください。
ポインタとアドレスの違いとは?
よく混同されがちな「アドレス」と「ポインタ」ですが、これら二つは違うものです。
まあ、どちらも同じようなものといえばそうなのですが、以下のように言い分けられます。
「アドレス」:メモリアドレスのことで、メモリに割り当てられた通し番号のようなもの。
「ポインタ」:「アドレス」と「データ型」を使用し、変数などを参照するもの。
つまり、「ポインタはアドレスを使用する機能」なのです。
「アドレス」と「ポインタ」はこのような関係にあります。
ごっちゃになるのは私も痛いほどわかります。(経験しました...。)
C/C++はアドレスを使ってプログラミングができる【ポインタ】
C言語やC++では、メモリアドレスを直接指定して操作することができます。
厳密にいえば、「データ型」と「メモリアドレス」を持つ「ポインタ変数」を作ることによってそれを可能にします。
詳しくは他の記事で解説しますが、軽くサンプルコードを見てみてください。
実行結果は以下のようになります。
変数aの値:1
変数aの値:2
このように、変数aの値を、ポインタ変数a_ptrを用いて変更することができています。
ポインタ変数a_ptrは「変数aの使用しているメモリ領域の先頭アドレス」と「int型(4byte)」という情報を持っているため、変数aが使用しているメモリ領域すべてがわかります。
このa_ptrを特殊な演算子「*」をつけて演算することで、変数aの値を間接的に変更することができます。
このような機能をポインタと呼びます。
このポインタの概念により、C言語/C++はとても自由度が高い設計になっていますが、逆にポインタによるバグも多く、学習難易度も高くなっています。(ほかの言語ではポインタという概念がなかったりする)
最後に
アドレスはポインタや配列の基礎となっている部分なので、しっかり理解しておくとこれからのプログラミングの学習がはかどるかと思います。
また、C言語の強みは「メモリ管理」にあるので、C言語をこれからもっと学んでいきたいのであれば、メモリとメモリアドレスの理解は欠かせないでしょう。
「ポインタ」「メモリアドレス」「配列」などの要素はどれがどれだか混乱しまくると思いますが、焦らずゆっくりと行きましょう。
僕のおすすめは、寝る前にメモリを数えながら寝ることですかね。
メモリが1つ、メモリが2つ...。
以上「【C言語/C++】ポインタや配列の基礎知識「アドレス」とは?わかりやすく解説!」でした!