Amazonで利用すべきコスパ最高のサービス一覧

【C言語/C++】ポインタや配列の基礎知識「アドレス」とは?わかりやすく解説!

この記事はこんな方にオススメ!
  • アドレスとは何かを知りたい方!
  • C言語/C++のポインタや配列の概念がよくわからない方!
  • 実際に変数がどのようにメモリ領域をとっているのか知りたい方!

今井

こんにちは!
C言語からプログラミングを学び始めた今井(@ima_maru)です。

今回は、「アドレス」の解説になります。

C言語学習者が躓くランキング上位の「ポインタ」を理解するために欠かせない概念です。

ポインタを理解するためにも、基礎知識となる「アドレス」の概念を理解していきましょう。

それでは解説していきます!

アドレスとは?メモリの番地?

アドレスとは、「番地」を意味します。

C言語C++などで使われる「アドレス」という単語は、「メモリアドレス」「メモリ番地」と同じ意味でつかわれます。

これ以降「メモリアドレス」として話を進めます。

結論からいえば、「メモリアドレス」とは、ひとメモリごとにつけられている通し番号のようなものです。

プログラムや変数は全てメモリに記録される?

本題に入る前に、少しパソコンの内部の話をしておきましょう。

パソコンには大きく分けて、以下のような部品があります。

パソコンの部品主な役割
中央演算処理装置
(CPU)
パソコンの脳の部分。CPUはあらゆるパーツを自在に操りパソコンを動かしている。
電源ユニットパソコンの心臓部分。安定した電力を供給します。
主記憶装置
「メモリ」
補助記憶装置よりも高速でデータの読み書きが行えるCPUがメインで使う作業スペースとなる。
補助記憶装置
(HDD/SSD)
主記憶装置と違い、電源を落としてもデータが消えない。比較的大容量。

その中でも、「メモリ」CPUが使う作業スペースのようなものです。

何らかのプログラムを実行するときは、このメモリにプログラムを読み込んで、それを読み書きしながら実行します。

当然、変数もこのメモリに記録されます。

つまり、変数はメモリのどこかに格納されているということになります。

そりゃどこかに値を記録しておかなければデータが消えてしまいますからね。

では、メモリの「どの位置にデータが格納されているのか」を知れたら便利ではないでしょうか。(というより、わからなくてはいろいろ困るんですけどね。)

例えば、

「この変数はこのメモリ番地を使ってまーす。」

というのがわかれば、

外部(呼び出した関数など)からでも直接変数の値を変えることができるようになります。

今井

「君、メモリのどこつかってる?」っていう情報こそが「メモリアドレス」なのです。

大前提として、コンピュータがプログラムを実行していくとき、そのプログラムや変数などがメモリ上に読み込まれているというのを覚えておいてください。

このイメージを持てているかいないかでは、C言語系のプログラミングの理解度も変わってくるはずです。

int型の変数がメモリ内に格納されるイメージを理解する

例えば、C/C++で以下のように変数を定義したとしましょう。

C/C++ 変数を定義
int main(void)
{
	// 変数aを初期化
	int a = 1;

	return 0;
}

そうすると、プログラムが動いたときのイメージは以下のようになります。

1行1行1byteのデータと考えてください。

メモリアドレスメモリを使用しているもの
0x000AB003???
0x000AB004int a 使用中
0x000AB005int a 使用中
0x000AB006int a 使用中
0x000AB007int a 使用中
0x000AB008???

今回の例では、0x000AB004 ~ 0x000AB007 までの4byteint aが使用しています。

もう少し詳しく言うと、以下のように4byte = 32bitを管理しています。

  • 0x000AB004 1~8bit目
  • 0x000AB005 9~16bit目
  • 0x000AB006 17~24bit目
  • 0x000AB007 25~32bit目

1つのメモリアドレスにつき8bit = 1byteの表現が可能で、int型4byteのデータ型なのでこのようにメモリを占有していることになります。

具体的な値を入れてみるとこうなります。(値がひっくり返ったりするので難しい)

メモリアドレスメモリ内のデータ(0bは2進数表現という意味)
0x000AB0030b ????????
0x000AB0040b 01000000
0x000AB0050b 00000000
0x000AB0060b 00000000
0x000AB0070b 00000000
0x000AB0080b ????????

4byte区切りで表すと以下のようなります。(少し難しい)

メモリアドレスメモリ内のデータ(0bは2進数表現という意味)
0x000AB0000b ???????? ???????? ???????? ????????
0x000AB0040b 00000000 00000000 00000000 00000001
0x000AB0080b ???????? ???????? ???????? ????????

直観だと2bitごとに確保されてひっくり返ってるのでわかりにくいですが、コンピュータの内部ではこうなっています。(その辺は正直わからくても問題ない)

どうでしょうか。

「int型の変数が実際にどのようにメモリ内に確保されるか」を想像できたでしょうか?

詳しくはあまり触れないですが、この感覚はC言語系のプログラミングではとても重要になってきます。

変数のメモリアドレスを確認する「アドレス演算子 &」

int型の変数がメモリの4byteを用いて表現されていることをさらっと解説しました。

そのことは今後データ型の解説で詳しくするとして、今回はそのメモリアドレスが実際にどんな値なのかを見ていきましょう。

データのアドレスを確認するには「&」を使います。(読み方はアンパサンドorアンド

これは、「アドレス演算子」と呼ばれ、データの前につけることで、そのデータが使用しているメモリの先頭アドレスの値に変換することができます。

先ほどの例で、int型の変数aを用いれば、「&a」とすることで先頭アドレス0x000AB004が得られます。

メモリアドレスメモリを使用しているもの
0x000AB003???
&a == 0x000AB004
(先頭アドレス)
int a 使用中
0x000AB005int a 使用中
0x000AB006int a 使用中
0x000AB007int a 使用中
0x000AB008???

注意が必要なのが、何バイト使っていようが返ってくるのは先頭アドレスのみということです。

今井

「4byte使ってるのに1byte分のメモリアドレスしか返ってこない…。」と勘違いしないように!返ってくるのは先頭アドレスだけなんだぞー。

サンプルコードで確認する前に、アドレス演算子についてまとめておきましょう。

アドレス演算子「&」とは?
  • 変数などの前につけて使用する。(例:&a)
  • 変数などが使用しているメモリ領域の先頭アドレスを返す。
  • 「&」の読み方は”アンパサンド”、または”アンド”。

以上のことを踏まえて、サンプルコードを見ていきましょう。

※C言語では出力の際にフォーマット指定子として「%p」(アドレス用)を用いています。

C言語 アドレス演算子の使い方
#include <stdio.h>

int main(void)
{
	int a = 1;
	printf("a:%dn", a);
	printf("&a:%pn", &a);

	return 0;
}
参考 コードを実行したい方はこちら!(C言語)Wandbox
C++ アドレス演算子の使い方
#include <iostream>
using namespace std;

int main(void)
{
	int a = 1;
	cout << "a:" << a << endl;
	cout << "&a:" << &a << endl;

	return 0;
}
参考 コードを実行したい方はこちら!(C++)Wandbox
実行結果
a:1
&a:00B5FD5C

実行結果はこのようになりました。

int型の変数aはメモリアドレス00B5FD5Cからの4byteを使っているんだなということがわかります。

がしかし!このメモリアドレスの値は実行するたびに代わります。

つまり、このメモリアドレスは実行時までわからないのです。

また、実行環境によっては、メモリアドレスの桁数が増えたりもします。

↓以下の環境では16進数12桁になっています。何回か試してみてメモリアドレスが動的に変わることを確認してみてください。

参考 コードを実行したい方はこちら!(C言語)Wandbox 参考 コードを実行したい方はこちら!(C++)Wandbox

ポインタとアドレスの違いとは?

よく混同されがちな「アドレス」「ポインタ」ですが、これら二つは違うものです

まあ、どちらも同じようなものといえばそうなのですが、以下のように言い分けられます。

「アドレス」:メモリアドレスのことで、メモリに割り当てられた通し番号のようなもの。
「ポインタ」:「アドレス」と「データ型」を使用し、変数などを参照するもの。

つまり、「ポインタはアドレスを使用する機能」なのです。

「アドレス」「ポインタ」はこのような関係にあります。

ごっちゃになるのは僕も痛いほどわかります。(経験しました…。)

C/C++はアドレスを使ってプログラミングができる【ポインタ】

C言語やC++では、メモリアドレスを直接指定して操作することができます。

厳密にいえば、「データ型」と「メモリアドレス」を持つ「ポインタ変数」を作ることによってそれを可能にします。

詳しくは他の記事で解説しますが、軽くサンプルコードを見てみてください。

C言語 ポインタ変数の使い方
#include <stdio.h>

int main(void)
{
	// 変数aを初期化
	int a = 1;

	// 変数aの値を表示
	printf("変数aの値:%dn", a);

	// ポインタ変数a_ptrに変数aのアドレスを代入
	int* a_ptr = &a;

	// ポインタ変数a_ptrで変数aの値を変更する
	*a_ptr += 1;

	// 変数aの値を表示
	printf("変数aの値:%dn", a);

	return 0;
}
参考 コードを実行したい方はこちら!Wandbox
C++ ポインタ変数の使い方
#include <iostream>
using namespace std;

int main()
{
	// 変数aを初期化
	int a = 1;
	
	// 変数aの値を表示
	cout << "変数aの値:" << a << endl;

	// ポインタ変数a_ptrに変数aのアドレスを代入
	int* a_ptr = &a;

	// ポインタ変数a_ptrで変数aの値を変更する
	*a_ptr += 1;

	// 変数aの値を表示
	cout << "変数aの値:" << a << endl;
}
参考 コードを実行したい方はこちら!Wandbox
実行結果
変数aの値:1
変数aの値:2

このように、変数aの値を、ポインタ変数a_ptrを用いて変更することができています

ポインタ変数a_ptrは「変数aの使用しているメモリ領域の先頭アドレス」と「int型(4byte)」という情報を持っているため、変数aが使用しているメモリ領域すべてがわかります

このa_ptrを特殊な演算子「*」をつけて演算することで、変数aの値を間接的に変更することができます。

このような機能をポインタと呼びます。

【C言語】ポインタを理解しよう!わかりやすくメリットを解説します!

このポインタの概念により、C言語/C++はとても自由度が高い設計になっていますが、逆にポインタによるバグも多く、学習難易度も高くなっています。(ほかの言語ではポインタという概念がなかったりする)

最後に

アドレスはポインタや配列の基礎となっている部分なので、しっかり理解しておくとこれからのプログラミングの学習がはかどるかと思います。

また、C言語の強みは「メモリ管理」にあるので、C言語をこれからもっと学んでいきたいのであれば、メモリとメモリアドレスの理解は欠かせないでしょう。

「ポインタ」「メモリアドレス」「配列」などの要素はどれがどれだか混乱しまくると思いますが、焦らずゆっくりと行きましょう。

僕のおすすめは、寝る前にメモリを数えながら寝ることですかね。

メモリが1つ、メモリが2つ…。

以上「【C言語/C++】ポインタや配列の基礎知識「アドレス」とは?わかりやすく解説!」でした!

今井

最後までご覧いただきありがとうございます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です