こんにちは、新卒の藤原です。
本投稿、[新卒が作る自作OS]では、我々が自作OSを作るにあたり、詰まったところや、備忘録的に残しておきたいところなどをまとめておこうという趣旨の投稿です。
間違っている認識も多々あるかとは存じますが、どうか温かい目で見てくださると幸いです。
C言語に触れるのは数年ぶりで、&と*の使い方など色々と忘れていました。そもそも理解していたかも怪しい。。。
覚えていたのは、変数のアドレスを格納した変数のことをポインタということくらい。
そこで、今回はC言語のポインタについてまとめようと思います。
昔、仲良くなれなかったポインタと仲良くなるぞ!
ポインタの宣言
ご存じの通り、変数の実体はメモリ上に載っており、メモリ上のどこに実体があるかを指すものがアドレスです。
ポインタは、このアドレスを格納する変数です。
ポインタは以下のように変数名の左に*をつけて宣言します。
int *p; ・・・①
ここではpというint型変数のポインタを宣言しています。
宣言時にはfig.1のようにメモリを確保します。
fig.1 ポインタ用メモリの確保
fig.1の例では、ポインタpを宣言することで、アドレス10から4バイト分のメモリを確保しました。(アドレスは適当です。)
この時確保されるメモリの大きさは変数によって異なり、データ型モデルで定義されています。
今回はint型変数もポインタも4バイトということにしました。
ポインタは通常の変数と同様に宣言のみでは、メモリが確保されるだけで値は不定です。
つまり、このポインタがどこのアドレスを指すポインタなのかは決まっていません。
直にアドレスを代入して初期化するか、次に紹介するアドレス演算子をつかって初期化する必要があります。
アドレス演算子&
アドレス演算子は変数のアドレスを取得する演算子です。
変数の前に&を付けることで変数が格納されているメモリのアドレスを得ることができます。
以下のように使います。
int *p; //ポインタの宣言 ・・・① int x = 5105; //int型の変数xに値を代入 ・・・② p = &x; //変数xのアドレスをポインタpに代入 ・・・③
①fig.1に示したようにポインタ用のメモリを確保します。
②fig.2に示すように、変数x用のメモリを確保し、5105(0x13f1)を代入、初期化します。
③fig.2に示すように、xにアドレス演算子&をつけることで、ポインタpに変数xのアドレスを代入します。
fig.2 ポインタへアドレスを代入
間接参照演算子*
間接参照演算子はポインタの値を取得する演算子です。
ポインタの前に*を付けることでポインタが示すアドレスに格納された値を得ることができます。
以下のように使います。
int y = *p; //int型の変数yに対して、ポインタpが示すアドレスに格納された値を代入 ・・・④
④fig.3に示すように、int型変数yのメモリを確保した後、間接参照演算子によってポインタpに格納された値を取得し、yに代入します。
fig.3 間接参照演算子を使って変数に値を代入
ポインタはどこで使う?
ポインタ渡しで配列や構造体のポインタを関数へ渡したり、ポインタとして渡した配列や構造体を返り値の代わりとして使ったりなどの使用方法があります。
しかし、OSの開発をしているとあまりこの辺りの話は重要な点ではないと感じました。
高級言語でも参照渡しとか配列を返すことは出来ますからね。もちろん使い方は知らなければいけませんが。。。
私が一番ポインタの力を感じたのは以下の場合です。
特定のアドレスの値を読み書きする場合
OSの開発をしていると、メモリの特定のアドレスを指定して読み書きしたい場合があります。
例えば、画面に描画する場合です。
fig.4はx86プロセッサのリアルモードにおけるメモリマップです。(こちらのサイトを参考にしました。)
fig.4 メモリマップ
画面に描画するためのVideo display memoryは0x000A0000から0x000C0000であることが分かります。
この中の番地を指定して値を書き込むことで画面に文字や絵を描くことができます。
このようなことはポインタが無ければできません。ハードウェアの仕様などが絡んでくるOS開発などには無くてはならない機能だと思います。
一方で、ポインタを使うと簡単に悪さをすることが出来てしまいます。
例えば、OSのシステムファイルの領域を指定して書き換えてしまえば、OSが動かなくなったりおかしな挙動を起こします。
こうしたメモリへの不正なアクセスを防ぐ機能もOSには必要となるわけです。
メモリへの直接的な参照ができるからこそ、組み込み系やOSなどの開発ではC言語が使われていることが良く分かりました。
逆に、アプリ開発では必要ないので、高級言語の多くではポインタの機能は隠蔽され使いやすくなっているわけですね。
まとめ
今回はポインタについて、宣言、アドレス演算子、間接参照演算子、使いどころの観点からまとめました。
- 宣言
変数名の左に*をつけて宣言
- アドレス演算子&
変数の前に&を付けることで変数が格納されているメモリのアドレスを得ることができる。
- 間接参照演算子*
ポインタの前に*を付けることでポインタが示すアドレスに格納された値を得ることができる。
- 使いどころ
メモリを直接参照して読み書きしたい場合に必須
OS開発に無くてはならないポインタと少しは仲良くなれた気がします。