Nodachisoft Nodachi Sword Icon
  
@あまじ✎ 2021年1月17日に更新

第2章35 ポインタを使ってみる

イチからゲーム作りで覚えるC言語
第2章34 ポインタを理解する前に最低限のC言語でのメモリ利用を知る : PREV
NEXT : 第2章36 ポインタと配列 :

この記事でやること

C言語の「ポインタ」についてお話していきたいと思います。ポインタのお話をする前提となる知識として、C言語からどのようにメモリを使っているかの基礎知識を前ページでまとめていますのでよければそちらもご覧ください。

値渡しと参照渡し

ポインタを使った一つの例を確認してみます。 例えばRPGなどでキャラクタがダメージを追うとき、体力を表す hitpoint の変数を減らす専用の関数を用意したいとします。 下のようにするとどうでしょうか。

 
NewToPointer1.c
#include <stdio.h>
void damage20(int hp) {
  hp = hp - 20; // 20 ダメージを与えたい!
}
int main() {
  int hitpoint = 100;
  damage20(hitpoint);
  printf("hitpoint は %d\n", hitpoint);
}

コードを実行すると、下のような結果になりました。

NewToPointer1.cの実行結果
hitpoint は 100

7行目で damage20 関数を呼び出しており、変数 hitpoint を関数に渡しています。 そして、 3 行目の damange20 関数の中で、変数 hp を 20 引き算しています。 その結果を 8 行目で printf で出力しています。ですが、ちゃんと欲しい結果である 80 になっていません。なぜこのような結果になるのでしょうか。

値渡し

関数に変数の値を渡すとき、関数の呼び出し側でセットした変数と、呼び出し先の関数の引数に置いた変数は別のものとなります。つまり、3行目の呼び出し元の hitpoint と、呼び出される側の変数 hp は別の変数であり、別のメモリアドレスを参照しています。

実際に関数を呼び出すとき、新しく変数 hp が作成され、hp に呼び出し元の hitpoint の値がコピーされます。(代入されます)

そのため関数の呼び出し先で hp の値を変更したとしても、呼び出し元の hitpoint の値はかわりません。

このような関数の呼び出し方を値渡しと呼びます。

参照渡し

呼び出した関数の先で、変数の中身を変更する例として、下のように書くことができます。

 
NewToPointer2.c
#include <stdio.h>
void damage15(int *hp) {
  *hp = *hp - 15; // 15 ダメージを与える
}
int main() {
  int hitpoint = 100;
  damage15(&hitpoint);
  printf("hitpoint は %d\n", hitpoint);
}

コードを実行すると、下のような結果になりました。

NewToPointer1.cの実行結果
hitpoint は 85

ちゃんと 15 ダメージ分が反映され、 hitpoin 変数の中身が想定通り減っています。

今回はdamage15関数の引数が

int *hp

となっています。int型の後に「*」アスタリスクがついています。これがつくことで、これはポインタを入れる変数であることを示します。変数 hp の中にはメモリアドレスを入れることができます。

その後、「*hp」と記載するとそれは変数 hp に設定されたメモリアドレスの中身を参照していることとなります。ですので、

*hp = *hp – 15;

と書くことで、変数 hp のメモリアドレスの中身を 15 引くという演算を行うことができます。

また呼び出し元も前回とは変わっています。

7行目では、damage15(&hitpoint); と記載しており、 変数 hitpoint の頭に「&」を付けています。前回のページでもお話しましたが、これは変数 hitpoint が格納されているメモリアドレスを意味し、アドレス演算子と呼ばれます。

たとえば hitpoint の値がメモリアドレスの 500 番に格納されているとき、&hitpoint の値は 500 となります。

この仕組みを使って、呼び出し側で変数のメモリアドレスを設定し、受取側でポインタ変数でメモリアドレスを受け取るようにすることができます。これにより呼び出し先の関数で、メモリアドレスの先を変更するなど演算が可能となります。

このような関数の呼び出し方、引数の渡し方を参照渡しと呼びます。 数値を渡す(呼び出し先の関数にコピーしている)ではなく、メモリの参照アドレスを渡す(呼び出し先の関数にコピーしている)ことをしているので参照渡しという呼ばれ方をしています。

ポインタ変数のサイズ

ポインタ変数には int 型だけでなく、char型や long 型なども指定可能です。書き方は同じように、

ポインタ変数の宣言例
int* hitpoint;
char* level;
long* exp;

のようにしてポインタ型変数を書くことができます。 これらのポインタ変数にはいずれもメモリアドレスが代入されます。 int型のサイズや char型のサイズは関係なく、どれも同じようにメモリアドレスが入るため、例えば 4 バイトなどのサイズとなります。 例えば下のようにすることで、メモリサイズを確認できます。

ポインタ変数のサイズ確認
int* hitpoint;
char* level;
long* exp;
printf("int*  は %dバイト\n", sizeof(*hitpoint));
printf("char* は %dバイト\n", sizeof(*level));
printf("long* は %dバイト\n", sizeof(*exp));

私の環境ではすべて 4 バイトとなりました。 OS が このプロセスに割り当てた仮想メモリを表現するために、4バイトが使われていることになります。 4バイトは負の数がなければ、2の32乗で4,294,967,296、約4GB ちょいも範囲を持てることになります。

ポインタ変数のサイズ確認結果
int*4バイト
char*4バイト
long*4バイト

ポインタ変数が型をもつ意味

ポインタ変数を宣言するときに int* や char* など、いずれもメモリアドレスが指定されていますが、これは参照するメモリアドレス先から読み取るメモリサイズを特定するために使われます。

例えば、short型は 2 バイトのメモリ領域を使ってデータを保存しています。(筆者環境)

ですので、short* で参照するメモリアドレスの先から 2 バイトぶんを読み取った数がメモリアドレス上に格納されている実際の数値ということになります。

short型のポインタを使用しているイメージ

あとがき

ポインタ変数について最初の導入部分をお話しました。 メモリアドレスという目に見えにくいものが登場するので、ポインタは少し理解しずらいかもしれませんが、使えるようになるとプログラムでできることがぐっと広がるかと思います。

非常に参考になったサイトさまや、参考文献など

ページの更新履歴

更新日 更新内容
更新なし
イチからゲーム作りで覚えるC言語
第2章34 ポインタを理解する前に最低限のC言語でのメモリ利用を知る : PREV
NEXT : 第2章36 ポインタと配列 :
 
 
送信しました!

コメント、ありがとうございます。

なんかエラーでした

ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。

Twitter:@NodachiSoft_jp
お名前:
 
連絡先:
 
メッセージ:
 
戻る
内容の確認!

以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください

お名前:
 
連絡先:
 
メッセージ:
 
Roboto からの操作ではないという確認のため確認キーを入れてください。
確認キー=95
戻る
 / 
送信確認へ
コメント欄
コメント送信確認へ

関連ありそうな記事(5件)です!

ゲーム等で使えるつなぎ目のないループするテクスチャ画像の作り方

#ツール#ゲームプログラミング✎ 2021-01-24
ゲームなどで使えるループ画像、パターンテクスチャのツール、手動での作り方をまとめ
広告領域
追従 広告領域
目次
第2章35 ポインタを使ってみる
第2章35 ポインタを使ってみる
この記事でやること
この記事でやること
値渡しと参照渡し
値渡しと参照渡し
値渡し
値渡し
参照渡し
参照渡し
ポインタ変数のサイズ
ポインタ変数のサイズ
ポインタ変数が型をもつ意味
ポインタ変数が型をもつ意味
あとがき
あとがき
非常に参考になったサイトさまや、参考文献など
非常に参考になったサイトさまや、参考文献など
ページの更新履歴
ページの更新履歴
Nodachisoft © 2020