MMGamesロゴ  MMGames
Twitterシェアボタン  Facebookシェアボタン   
しんで覚えるC言語
しんで覚えるC言語

変数とメモリの関係

変数はメモリ上に存在する
第5章での変数の説明時に、変数はメモリに作られると説明しました。
しかし、その具体的な内容についてはまったく説明してきませんでした。
それは、変数を扱うだけなら、そこまで知る必要がなかったからです。

しかし、この先で取り扱うポインタという概念を理解するためには、
変数がどのようにメモリに格納されているかを知っておく必要があります。

前節で説明した通り、コンピュータのメモリとは、巨大な1列ロッカーのような構造です。
そして、このロッカーは、1つ1つがオンとオフの2つの状態を記憶しています。
このロッカー1つを1ビット、8つをまとめて1バイトと呼んでおり、
メモリには、この1バイト毎に番号がつけられて区別されているのです。

プログラムで宣言した変数も、このメモリに番号付きで記憶されています。
ただ、番号で区別するのではわかりにくいので、名前をつけているのです。
実行ファイルにコンパイルすると、変数名は番号に変換されます。

要するに、すべての変数はメモリに作られており、
そこでは、番号をつけて区別しているということをまずは頭に入れてください。

C言語はこんな言語である
以後、この章では、C言語の実態に迫る展開が多々見られます。
今までの章で説明されたなかったことの多くがこの章で明らかにされます。
その中では、C言語の文法などを批判するような展開も多く見られます。

そもそも、C言語は、デニス・リッチーという人物が、
自分達で使うためだけに作ったプログラミング言語であって、
世界中で使われるようになるとはまったく考えていなかったのです。
その為、作った人の都合に合わせたような部分が見られ、
普通の人々には不親切な文法の言語になっています。

しかし、それでも、C言語は世界中で使われているのです。
所々不可解な部分はありますが、もっとも汎用性のある言語であり、
小さな部品から、ゲームプログラムまで、幅広く使用されています。

メモリ上の番号を表示する
前項では、すべての変数はメモリに作られて、番号をつけて区別していると説明しました。
実は、変数につけられた番号は、プログラムで調べることができるのです。

番号を調べるのは意外に簡単で、printf関数で%p指定子を使うだけです。
ただし、変数名の前には、&をつける必要があります。
次のプログラムは、int型変数iの番号を表示する例です。

ソースコード
#include <stdio.h>

int main(void)
{
    int i;
    printf("%p\n", &i);
    return 0;
}

このプログラムの実行結果は、次のようになるかもしれません。

実行結果
0012FF80

これは筆者の環境で実行した結果ですが、結果の数字は、
パソコンや使用するコンパイラによって異なります。

指定された変数をどの番号のメモリに割り当てるのかは、
コンパイラ(正確にはリンカ)が自動的に決めてしまい、
さらにその値もOSの仮想メモリ機能によって変化するからです。

この番号は16進数という表示方式です。10進数にすると 1245056 となります。
なぜ、16進数のようなわかりにくい方法で表示されているのかと言いますと、
前項で説明した通り、コンピュータの数値は2進数で記憶されています。
2進数では、 2、4、8、16、・・・・と桁があがっていきます。
10進数では、10、100、1000、・・と桁があがっていきます。
16進数では、16、256、4096、・・と桁があがっていきます。

これを見ると気づくかもしれませんが、10進数と2進数はうまくかみ合いません。
16進数は、2進数とうまくかみ合うため、都合が良いのです。

コンピュータはなぜ2進数?
突然ですが、あなたは両手の指10本でいくつまで数を数えられますか?
普通は10までですが、実は、理論的には1024まで数えられます。
指一本を2進数1桁に対応させて数えればいいのです。
この様に、10進数ではなく2進数で数えた方が、
回路を小型にできます。

とはいえ、ここでは、16進数うんぬんはさほど重要なことではありません。
重要なことは、変数につけられた番号を簡単に調べることができる点です。
この、変数につけられた番号のことを、アドレスと呼んでいます。

キーワード
【アドレス】

変数につけられたメモリ上での番号のこと。


これは、URLアドレスと言うようなアドレスとまったく同じ意味と考えて問題ありません。
変数の住所を示す番号が、すなわちアドレスなのです。
複数の変数の番号
前項では、1つだけしか調べなかったので、今回は複数の変数のアドレスを調べてみます。
繰り返しますが、変数につけられたロッカー番号のことを、アドレスと呼んでいます。
次のプログラムは、3つのint型変数のアドレスを表示する例です。

ソースコード
#include <stdio.h>

int main(void)
{
    int i1, i2, i3;
    printf("i1(%p)\n", &i1);
    printf("i2(%p)\n", &i2);
    printf("i3(%p)\n", &i3);
    return 0;
}

このプログラムの実行結果は、次のようになるかもしれません。

実行結果
i1(0012FF78) 10進数では1245048
i2(0012FF7C) 10進数では1245052
i3(0012FF80) 10進数では1245056

これは筆者の環境で実行した例です。数値は環境によって異なります。
どうやら、int型変数には4番ずれの連番が割り当てられているようです。
これは、int型のサイズが4バイトだからに他ありません。

4バイトとは、4x8=32なので、32ビット、2進数32桁となります。
マイナスを含まない場合には、0から4294967295(約43億)までの数値を記憶できます。
2バイト(16ビット)の場合には、0から65535までとなります。

int型以外の変数もコンパイラ毎にサイズが異なっています。
ただし、char型だけは、必ず1バイトと決まっています。
char型には0~255の範囲内の文字番号を入れておくために、
必ず255まであるようにしておかないと何かと不都合だからです。
いにしえのパソコンの中にはcharが7ビットというものも存在したらしいですが・・・

今回は連番になりましたが、変数のアドレスはいつでも必ず連番とは限りません
なぜなら、この3つの変数は別々に使われる変数なので、
バラバラの場所にあっても何も問題はないからです。
コンパイラによっては逆順になったりすることもあります。
配列の番号
変数の番号同様、配列もアドレスを表示できます。
しつこいですが、アドレスとは、変数につけられたロッカー番号のことです。
配列の場合には、先頭に&をつける必要はありません。
ただし、配列の個々の要素は変数と同じ扱いなので、当然&をつけます。
次のプログラムは、配列とその要素のアドレスを表示する例です。

ソースコード
#include <stdio.h>

int main(void)
{
    int array[10];
    printf("array___(%p)\n", array);
    printf("array[0](%p)\n", &array[0]);
    printf("array[1](%p)\n", &array[1]);
    printf("array[2](%p)\n", &array[2]);
    return 0;
}

このプログラムの実行結果は、次のようになるかもしれません。

実行結果
array___(0012FF5C) 10進数では 1245020
array<0>(0012FF5C) 10進数では 1245020
array<1>(0012FF60) 10進数では 1245024
array<2>(0012FF64) 10進数では 1245028

どうやら、配列の場合も、4バイトずつ連番に割り当てられているようです。
さらに、配列名を指定した場合、配列の最初の要素と同じ番号になるようです。

実は、ここで、配列のトリックのすべてが暴かれています。
実は配列名は、配列の最初の要素のアドレスを表していたのです。
各要素を参照する時に、[0],[1] と言った要素番号をつけましたが、この意味は、
配列名のアドレス+要素番号のメモリを参照するという意味になるのです。

つまり、最初のアドレスを決めておけば、それに番号を足し算することで、
たくさんの変数が並んでいるという状態を表現することができるのです。

ただ、ここで少し不思議なのは、要素番号1の時、実際のアドレスは4増えていることです。
この謎は、int型のサイズを思い出していただければ、すぐに解けるでしょう。

32ビットコンパイラでは、int型のサイズは4バイトです。
つまり、1つ先のint型の変数のアドレスは、最初のアドレス+4となるのです。
同様に、2つ先のint型の変数のアドレスは、最初のアドレス+8となります。

この配列の仕組みについては、後ほどさらに詳しく説明いたします。


本サイトについて

苦しんで覚えるC言語(苦C)は
C言語入門サイトの決定版です。
C言語の基本機能を体系立てて解説しており、
市販書籍と同等以上の完成度です。

第0部:プログラム概要編
  1. プログラムとは何か?
2章:プログラムの書き方
  1. 書き方のルール
  2. 書き方の慣習
  3. 練習問題2
3章:画面への表示
  1. 文字列の表示
  2. 改行文字
  3. 練習問題3
6章:キーボードからの入力
  1. 入力用の関数
  2. 入力の恐怖
  3. 練習問題6
9章:回数が決まっている繰り返し
  1. 繰り返しを行う文
  2. ループ動作の仕組み
  3. 練習問題9
10章:回数がわからない繰り返し
  1. 回数不明ループ
  2. 入力チェック
  3. 練習問題10
13章:複数の変数を一括して扱う
  1. 複数の変数をまとめて扱う
  2. 配列の使い方
  3. 練習問題13
20章:複数のソースファイル
  1. 最小限の分割
  2. 分割の定石
  3. 練習問題20

コメント
COMMENT

💬 コメント投稿欄を開く