MMGamesロゴ  MMGames
Twitterシェアボタン  Facebookシェアボタン   
 English 

しんで覚えるC言語
しんで覚えるC言語

アドレスを記憶する変数

ポインタという単語
前節では、関数に変数のアドレスを渡すと変数の中身を変更できると説明しましたが、
それには、アドレスの値を記憶することのできる変数が必要になります。
それがなくては、アドレスの値を引数で渡しても受け取れません。

世間では、アドレスの値を記憶する変数をポインタと呼んでいますが、
これは本当はあまり正確な呼び方ではありません。
なぜなら、ポインタとは、アドレスを扱う機能3つの総称だからです。
ポインタという呼び方は総称であり、正確には3種類に別れています。

1つ目は、ポインタ型です。
今まで出てきたint型やdouble型と同じような型です。
ただし、ポインタ型の場合、それらとは少し異なる特徴があります。

2つ目は、ポインタ値です。
これは、ポインタ型で扱える数値、要するにアドレスのことです。
整数や実数といった数値の区別と同様に、ポインタ値という区別があるのです。

3つ目は、ポインタ変数です。
これは、ポインタ型で宣言された、ポインタ値を記憶できる変数のことです。
int型の変数やdouble型の変数と基本的には同じことです。

次からは、これら3つの違いについての説明をしますので、
こんがらがってしまわないように気をつけて読んでください。
ポインタ型
ポインタ型とは、アドレスを記憶する変数の型のことを意味しています。
ポインタ型は、通常の型とは異なる側面を持っています。
それは、ポインタ型は、他の型から作り出される派生型であるという点です。

なにやらわかりにくい説明ですが、実際にはそんなに難しいことではありません。
たとえば、int型の場合、これは独立した型です。他の型とは何の関係もありません。
それに対して、ポインタ型は、他の型とポインタ型を合体させて作ります。

int型とポインタ型を合体させると、intへのポインタ型という型ができます。
double型の場合なら、doubleへのポインタ型が作り出されます。
また、intへのポインタ型にさらにポインタ型を合体させて、
intへのポインタのポインタ型という、多重のポインタ型を作ることすらできます。

要するに、ポインタ型は、他の型と合体しなければこの世には存在出来ないという、
いわば、寄生虫のような型なのです。

ところで、ポインタ型は、要するにアドレスを記憶する変数の型です。
アドレスは、この章の始めで表示させたように、結局は整数値に過ぎません。
その整数値を記憶する型が、どうして他の型と合体する必要があるのでしょうか。

これには明確な理由があります。指定アドレスに記憶された数値を取り出すためです。
たとえば、int型、double型、char型は、すべてサイズが違っています。
int型の場合、標準的なコンパイラでは4バイトのサイズですから、
億単位で存在するメモリというロッカーの4個分を使っていることになります。
int型の値を取り出すには、この4個をまとめて取り出さなくてはいけません。

型によっては、メモリに記憶された2進数の読み方まで違ってくることもあるので、
元々の型がなんだったのかがわからない限り、記憶された数値を取り出せないわけです。

つまり、ポインタ型は、どんな型の変数のアドレスだったかわかる必要があります。
そこで、あらかじめ他の変数と合体した形でポインタ型として作っておけば、
そのポインタ型の変数に記憶された数値は、合体されている型であるとすぐわかります。

voidポインタ
実際には、単独で存在するポインタ型としてvoid型があります。
この型は、どんな変数のアドレスでも記憶できますが、
元々の変数の型がわからないため、値を取り出すことができません。

ポインタ値
ポインタ値とは、ポインタ型の変数が記憶できる数値のことですが、
これは、要するに、変数のアドレスの値のことです。

多くのコンパイラでは、ポインタ値とは、単なる符号なしの整数値に過ぎません。
その理由は、コンピュータのメモリの仕組みを思い出せばすぐにわかるでしょう。
コンピュータのメモリは、何十億というロッカーの集まりであり、
アドレスとは、そのロッカーにつけられた番号であるからです。

しかし、ここで1つの疑問が残ります。
アドレスが単なる整数値であるなら、int型に記憶すれば事足りるはずなのに、
なぜ、わざわざポインタ値などという新しい数値として扱う必要があるのでしょうか?

実は、int型に記憶すれば良い、というのは、ある意味ではその通りとも言えます。
32ビットのコンパイラでは、アドレスもint型も32ビットになるので、
符号なしのint型に記憶した所で、何の問題もありません。
それどころか、C言語の先祖であるB言語では、
C言語のint型にあたる型にアドレスを記憶する仕組みだったのです。

しかし、C言語でポインタ値という数値として扱われているのには理由があります。
まず、ポインタ値と通常の整数値とでは、その意味が明らかに異なります。

通常の整数値は、プログラムの中で計算などを行うための数値ですが、
ポインタ値は、計算に使われる数値ではないからです。
つまり、この2つはどちらも単なる整数値ですが、その目的はまったく別なのです。

このことから、int型の変数で両方を扱っても、何のメリットもありません。
むしろ、変数に記憶される数値が整数値なのかポインタ値なのかわかりにくくなります。
であれば、いっそのこと、2つを別々の数値とした方がよほど便利になります。
ポインタ変数
ポインタ変数とは、ポインタ型で宣言された実際の変数のことです。
この変数には、その元となった型の変数のアドレスを自由に代入できます。
さらに、記憶しているアドレスのメモリを読んだり書き換えたりできます。

このことから、今までの変数とはかなり違った性質を持つことがわかります。
実際、ポインタ変数は今までの変数にはない機能を備えています。

他の変数は、どんな値を記録するにせよ、なんらかの計算に使うことが目的でした。
文字型のcharですら、その実態は文字番号であり、どの文字を表しているのかを計算するための変数です。

しかし、ポインタ変数では、記憶しているアドレス値を計算に使うことはありません。
ポインタ変数の役目は、それが指し示しているアドレス番号のメモリの値を計算することです。
ポインタ変数そのものを計算に使うのではなく、それが指し示している変数を計算するのが、ポインタ変数の目的です。

言い換えると、普段はポインタ変数として振る舞っているのですが、
指し示している変数の計算が必要な時には、普通の変数に変身する必要があるのです。

この変身機能こそが、ポインタ変数の最大の特徴となります。
ポインタ変数は、ポインタ変数モードと通常変数モードの2モードを備えており、
必要に応じて、それぞれを切り替えて使うことができるのです。

ポインタ変数モードでは、たいした機能は備えていません。
具体的には、アドレスへの代入と足し算引き算だけしかありません。
なぜなら、ポインタ変数モードに必要なのはアドレスの記憶だけだからです。
アドレスさえ記憶していれば、後はとくに何もする必要がありません。

通常変数モードに切り替わった場合、その性質は通常変数とまったく同じになります。
おかげで、通常の変数と同様にさまざまな演算子を使って計算できます。
当然、その時に使われるメモリは、ポインタ変数モードで記憶したアドレスになります。

今までの変数は、ただ通常変数として使えば良かったのですが、
ポインタ変数では、2つのモードを適切に切り替えて、
使い分けなくてはいけないことを覚えておいてください。


本サイトについて

苦しんで覚える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

💬 コメント投稿欄を開く