C言語/ポインタ

オブジェクトはメモリー上に領域が確保され、値が格納されます。 メモリーをバイト単位の一次元配列と考えた場合、その配列の添え字がアドレスです。 バイト単位の一次元配列であることを強調するため、バイトアドレスとも呼ばれます。 1バイトは最小でも8ビットで、そのビット数はlimit.hのマクロCHAR_BITで定義されています、10進数で256(2)通りの値を格納できます。256通り以上の値を格納するに連続した複数のバイトを一塊として使用します。
オブジェクトのアドレスを取得するには、オブジェクト名の前に&演算子(アドレス演算子)を付けます。
この様に、オブジェクトには変数の他、関数も含まれます(アドレスの値は実行条件によって異なります)。
は、main関数のアドレスを得る式で、単に
でも同じ意味です。この様に関数もメモリー上のオブジェクトなのでアドレスを持ちます。

このように、ポインタはきっちりとアドレスを受け継いでいることが分かります。 このことから「ポインタとは単にアドレスを格納するための型である」と思ってしまいがちですが、指し示す型の大きさや符号の有無(配列なら要素数や次数)などの特性おも示していることに、注意が必要です。
下記コードのように、ポインタ型変数を一切宣言しなくても、アドレスの情報をもとにして最初の数を復元することが可能です。 ポインタ型の知識はいったん置いといて、下記のコードを実行してみましょう。
このように、ポインタ型変数を全く宣言しなくても、アドレス情報をもとにすることで最初の値(例では1234)を復元することが可能です。ポインタの学習で悩んだら、これらの基本例に立ち返りましょう。 上記例では説明の都合上、printfに引数に&演算と*演算を使いましたが、下記コードのように何もprintfの引数でなくとも、&演算と*演算は型宣言時に行わなくても使用可能です。
ポインタの機能として、ポインタ変数を宣言する事によって、なんらかの別の変数のアドレスを格納する事ができるだけの領域を確保します。 ポインタ変数がその変数の値として格納するものは、指し示すべき対象のオブジェクトが格納されたアドレスです。 ポインタ変数の宣言では、指し示すべき対象のデータの型を指定します。 このようなメモリー上のデータの記憶位置を指定するデータである「アドレス」を格納するための変数のことをポインタ変数といいます。 上記コード例のようにC言語では、変数宣言において、変数名の直前に「*」を伴って宣言された変数は、(その*よりも前に書かれた型のデータを指し示す)ポインタ変数の宣言になります。 ポインタ変数の名前は、他の変数の名前と同様に有効な識別子でなければいけません。
ポインタpの指し示すオブジェクトの型は int であることは確定していますが、ポインタ変数pの値は不定で p を使って参照することは不正です。 そこでポインタ変数の宣言と同時に初期値を与えることが強く推奨されます。 初期値として適切な式が即座に思いつかない時にはNULLで初期化することが(ワーストケースを避けるため)妥当です。
とすれば、p を NULL で初期化出来ます。 NULLは、どの様な型へのポインタとして有効な値で、なおかつ有効なアドレスではないことが言語仕様で保証されています。 このことで、NULLを使った参照を実行時に捕捉できる可能性が出てきます(実行時にNULLによる参照を捕捉できるかは言語処理系と実行環境依存です。一般に、UnixではSIGSEGVが、Windowsでは構造化例外処理による例外が上がりますが、0番地からどの程度離れた番地への参照を捕捉するかなどでバリエーションがあります。)。
では、ポインタ変数の名前は junk234 です。
このように、ポインタ変数pの値として、整数変数iのアドレスが格納できている事が分かります。
ポインタへアドレスを格納
「*ポインタ名」を使って、そのアドレスの指している先の変数の値を書きかえることもできます。
ポインタを使って変数を書き換える場合、書き換えられる変数は、そのポインタに代入されているアドレスに対応する変数が書き換えられるのです。
変数の書き換え処理の記法は、具体的には後述するように代入を使って、書き換えが行われます。
さて、 ポインタを使って、 ポインタが指す変数に値を間接的に代入するには、次のように*演算子を用います。
書き換え後
このように、確かに変数iを55から1234に書き換えています(printfで出力している変数はiです)。
上記のプログラム中では、一度も「i = 1234」とは記述されていないにもかかわらず、実行結果では、printfによる変数iの表示で「1234」の表示が行われています。このことから、ポインタ(上記コードではpi)を使用して「*pi = 1234」のように書くことで、ポインタの指し示す変数(上記コードではi)に代入をする事ができる事が分かります。
ポインタを介した代入
ポインタを使って、 ポインタが指す変数の値を間接的に参照するには、次のように*演算子を用います。
一般に、「*ポインタ名」は「変数名」と同じものを表しています。なので、上記のコードの場合、 printf("%d", *pi) とは、つまり、printf("%d", i) のことなので、よってprintfで「1234」を表示します。
「ポインタ」という用語は、文脈によってポインタ型、ポインタ型の変数、ポインタ型の変数の値のいずれかを指す。 世間ではそれらをまとめて「ポインタ」と呼んでいるので、何を指すかは文脈で判断します。
ポインタ型はある型から導出し、 その型の実体を参照するための値(一般にアドレス)を持つ。 つまり、ポインタ型は「ポインタ型」として単体であるのではなく、 あくまで他の型から導出して作られます。 ある型Tから導出したポインタ型を「Tへのポインタ」と呼びます。 つまり、intへのポインタや、doubleへのポインタなどがあるわけです。
ポインタ型の変数は、 別のオブジェクトへのアドレスを格納し、 そのアドレスの指している先のオブジェクトに、 間接的にアクセスできます。
ポインタ型の変数の値は、 前述したように一般にアドレスです。 ただしアドレスなら何でもいいわけではなく、 そのアドレスはオブジェクトを指している必要があります。
さて、ポインタの宣言の記述は次のようになっています。
ポインタのデータ型とポインタ名は、 変数の宣言のときと同様です。 同じデータ型へのポインタは「,(コンマ)」で区切って一行で宣言できます。 その場合は、各々のポインタ名の前に*をつけること。
複数のポインタを一行で宣言するとき、しばしば下の例のような誤りを犯す場合があります。
上の例はtypedefを用いて、下の例のように記述することができます。

アドレス値は加減算が可能です。このことを利用して、計算によって目的のアドレス値を指定したり、配列の添字ベースの距離を求めたりできます。
このように、ポインター同士のの引き算は、バイトアドレスの差ではなくポインターの指し示すオブジェクトの型を要素とする配列の添え字に相当する整数値をとります。
関数も変数などと同じメモリー上のオブジェクトなのでアドレスを持ち、そのアドレスをポインター変数に保存し、ポインター変数を介して呼び出すことができます。
標準ライブラリ関数の qsort() の最後の引数も関数へのポインターです。
別のポインタを指すポインタを作ることができます。
ポインタを指すポインタを宣言する時には、 ポインタ名の前に*単項演算子を1つ多くつけます。
ポインタを指すポインタが指しているポインタが指しているオブジェクトにアクセスするには、 ポインタを指すポインタ名の前に*単項演算子を1つ多くつけます。
ppiが間接的に指す変数iに値1234を代入します。
void型へのポインタを宣言することができます。 void型へのポインタは、他の型へのポインタを格納できます。 間接演算子を使うときは型キャストして用います。 void型へのポインタはインクリメントやデクリメントと整数との加減算は出来ないです。
ポインタは、4つの算術演算子(+, ++, -, --)を使って、整数を加算減算することができます(void型へのポインタは除く)。 ポインタにn加えると、ポインタのバイトアドレスは「そのポインタの型のサイズ*n」だけ進む。
ポインタの演算は、ポインタが配列や配列の要素を指す場合によく使われます。
ところで、ポインタが指しているオブジェクトには、ポインタを使って、普通の変数と同様に参照できます。 その際、演算の優先順位には注意が必要です。
上の記述では、pをインクリメントし、 そのアドレスにある値を返す。
上の記述では、pのアドレスにある値をインクリメントします。
ポインタをconst修飾子による定数化と組み合わせて用いたい場合、少々、下記のような複雑な問題があります。
問題の概要は、
などの事情です。
このため、下記のような通常のコードでも、コンパイラによっては警告文が出ます(コンパイル自体は可能です。参照先を書き換えようとはしていないので)。
上記コードで警告が出る場合、MinGWの場合なら、それは下記のようにaとpaの型の不一致が原因です。 コンパイラ内部では、const int宣言された変数は、int型ではなくconst int型という別種の型で宣言されているからです。なのでポインタ pa のほうも const int 型で宣言しなければなりません。
ただしこの場合、ポインタ宣言時にconst をつけたにもかかわらず、下記のようにポインタの指し示す先を変更できてしまいます。しかも警告表示は一切出ません(MinGWで確認)。
ポインタ変数自体を書き換えたくない場合、int* const paのように int* と変数名のあいだにconstを書きます。
エラーになる理由は、定数として宣言されたポインタ変数 pa を書き換えようとしたからです。
上記コードでは、アスタリスク( * )の前にconstがあるのか、アスタリスクの後ろにconstがあるのかで、区別しています。
よって下記コードのように、int const *paのようにアスタリスクの後ろにconstをつければ、コンパイル可能です。
int aのようにaやbにconstをつけずにint const *paのように宣言した場合、ポインタ変数paだけが定数になるので、aやb自体の書き換えは可能です。ただし、コンパイラによっては動作が不安定かもしれないので、なるべくなら避けたほうが安全です。