みえるCお試し版

トラック 10・メモリの仕組み

アドレス——すべての箱に番地がある

読む目安 約12分 ねらい: 変数の番地がいつ決まるか、先頭バイトを指すこと、隣の変数との差がサイズになることを説明できる

このレッスンの要点

  • 番地は変数を作った瞬間に割り当てられ、実行のたびに変わりうる
  • 変数の番地は「先頭バイトの番地」を指す
  • 隣り合う変数の番地の差は、その型のバイト数と一致する
  • 図では番地を 0x1000 から始まる読みやすい値で書く。本物の番地はもっと長く、動かすたびに変わる

前のレッスンで、メモリのロッカーには1つずつ番地が振られていると学びました。ここでは、その番地がいつ・どうやって決まるのかを見ていきます。

番地は変数を作った瞬間に決まる

int a = 10; と書くと、コンパイラとOSが協力して「この変数はここに置く」という場所を決めます。プログラマは番地を指定しません。空いているロッカーを係の人が割り当ててくれる、と考えてください。

決まった番地は、そのプログラムが動いているあいだ変わりません。ただし次に実行したときは別の番地になることがよくあります。番地の値そのものを覚えることに意味はなく、大事なのは「どの変数がどこにいて、誰と隣り合っているか」という関係のほうです。

ビット番地って毎回変わるの? じゃあ覚えても意味ないじゃん
ポインタ姫ええ。ですから郵便屋のわたくしも、番地を暗記するのではなく「そのつど調べる」のですわ。調べ方は t10-05 でお教えします

変数の番地は「先頭のロッカー」の番地

int は4バイト、つまりロッカー4個ぶんを使います。では a の番地はどれを指すのでしょうか。答えはいちばん小さい番地(先頭のロッカー)です。

たとえば a が 0x1000 番地から始まるなら、a は 0x1000・0x1001・0x1002・0x1003 の4個を占領し、a の番地は 0x1000 です。そして次に作られる int b は、その先の 0x1004 に置かれることが多くなります。

int a = 10;   // 0x1000 から4バイト
int b = 20;   // 0x1004 から4バイト
char c = 'X'; // 0x1008 から1バイト

隣り合う int の番地の差が4になるのは、int が4バイトだからです。番地の差=そのぶんのバイト数という感覚は、この先ずっと使います。

ただし、変数がこの順に隣り合って並ぶ保証はありません。コンパイラは速度のために順番を入れ替えたり、すきま(パディング)を入れたりします。確実なのは配列だけで、配列の要素は必ず隙間なく連続して並びます。ふつうの変数どうしの位置関係は「たいていこうなる」程度に受け取ってください。

変数が積まれていく様子を見る

言葉だけでは掴みにくいので、メモリの図で実際に積まれていく様子を見てみましょう。「1行すすめる」で1行ずつ進みます。

ここにはメモリの図が入る。アプリの中では、1行ずつ動かして見られる。 アプリでこのレッスンを開く

この教材の図では、番地を 0x1000 から始まる読みやすい値で書きます。本物の番地は 0x7ffd5a20 のようにもっと長く、動かすたびに変わります。本物の桁数を並べても読みにくいだけで、学ぶうえで大事なのは「どれとどれが同じ番地か」「となりとどれだけ離れているか」だけだからです。ヒープ側の箱の番地は 0x8000 から始まります。

関数が終わると番地は返される

メモリの図の最後で、main が終わると変数の箱がまとめて消えたはずです。使い終わったロッカーは「空き」に戻り、次に誰かが使えるようになります。中身が消毒されるわけではなく、次の人が上書きするまで古いデータが残っているという点も覚えておいてください。この性質が、のちのちバグの温床になります。

まとめ

番地は変数を作った瞬間に割り当てられ、実行のたびに変わりえます。変数の番地は先頭バイトの番地を指し、隣の変数との差はサイズと一致します。番地は「覚えるもの」ではなく「調べて使うもの」です。

アプリでこのレッスンを動かして学ぶ(無料・登録なし)

動かして確かめるコード

#include <stdio.h>

int main(void) {
    int a = 10;
    int b = 20;
    char c = 'X';
    printf("%p\n", (void *)&a);
    printf("%p\n", (void *)&b);
    return 0;
}

確認問題

確認問題 1

番地についての説明で、まちがっているものはどれでしょう。

  1. 同じプログラムを2回実行すれば、変数の番地は必ず同じ値になる
  2. 変数の番地は、その変数が使う先頭バイトの番地を指している
  3. int が4バイトの環境では、配列の隣り合う要素の番地は4ずつ増える
  4. 番地は変数が作られた時点で決まり、その変数が生きているあいだは変わらない
答えを見る

正解: A(同じプログラムを2回実行すれば、変数の番地は必ず同じ値になる)

最近のOSは安全のために配置を毎回ずらすので、実行のたびに番地が変わるのがふつうです。ですから番地の数値そのものを覚えることに意味はなく、「誰と誰が同じ番地か」「どれだけ離れているか」という関係を見ます。残り3つはすべて正しい説明です。

確認問題 2

int は4バイト、a は 0x1000 番地から置かれ、b はその直後に置かれました。表の空欄を埋めてください(番地は 0x1000 の形式で答えます)。

int a = 10;
int b = 20;
a = a + b;
b = a - b;
行a の値b の値a の番地b の番地
110-0x1000-
210200x10000x1004
3?200x1000?
43010?0x1004
答えを見る

空欄の答え(上から順に): 30、0x1004、0x1000

値は代入のたびに変わりますが、番地は変数が作られた時点で決まり、そのあと変わることはありません。int は4バイトなので、b の番地は a の4つ先の 0x1004 になります。「値」と「場所」は別物、という感覚がポインタの土台です。

「メモリの仕組み」の目次

  1. メモリとは巨大なロッカー群
  2. アドレス——すべての箱に番地がある
  3. プログラムのメモリレイアウト有料版
  4. スタックフレームを覗く有料版
  5. 変数の番地を見る——&演算子有料版
  6. バイトとエンディアン有料版
  7. メモリの図の使い方有料版

この先へ ポインタ・メモリ・構造体・ファイル入出力まで、全26トラック・211レッスン・演習508問は有料版に入っている。

有料版(BOOTH)を見る

ほかのトラック

姉妹品: 手を動かして覚える Linux 教材 Linux とインフラの仕組みを地図で学ぶ

Web 版 / BOOTH