Nodachisoft Nodachisoft logo, Katana Sword Icon
  
@あまじ✎ 2021年1月18日に更新

第2章38 ポインタ変数への足し算

第2章38 ポインタ変数への足し算
イチからゲーム作りで覚えるC言語
第2章37 char型ポインタ変数と文字列の操作 : PREV
NEXT : 第2章39 ヌルポインタ(NullPointer) と Void について :

この記事でやること

C言語でポインタ変数への足し算をしたとき、どんな処理が行われているかを把握してきましょう! ポインタの指すメモリアドレスを直接操作することは若干分かりづらく、バグの原因となりやすいので注意しながら扱う必要があります。

自分でプログラミングする時には、多用することは避けたいところですが、 他の人が書いたソースコードの動きを把握するために理解はしておきたいところです!

配列とメモリアドレスの操作

C言語で変数の配列とポインタは深い関係があります。

配列は下の用に定義でき、配列の要素にアクセスするために、添え字を指定します。(詳細は以前のページ参照)

添え字例
char playername[6];
playername[0] = 'p';
playername[1] = 'l';
playername[2] = 'a';
  :
playername[4] = 'c';
playername[5] = '\0';

配列の中身を、定義するときに一緒に指定して初期化することもできました。

char playername[] = "planc";

char 型のポインタ

前回、文字列をポインタ変数で扱ったときの仕組みを記載しました。 今回はポインタ変数への足し算、引き算をしたとき、どのような動きになるかを確認していきます。

 
charpointer.c
#include <stdio.h>
int main(){
  char *playername="tairyokuga 0 desu.";
  for ( int i = 0 ; playername[i] != 0 ; i++ ) {
    printf("%c", playername[i]);
  }
}

実行すると、下のような結果となります。

結果
tairyokuga 0 desu.

playername はポインタ変数で、「tairyokuga 0 desu.」の文字列の頭である文字「t」への メモリアドレスを持っています。

実行した結果から、playername[i] は playername のメモリアドレスから、i バイト分、加算した位置の メモリのデータへのアクセスをしていることがわかります。

配列の n 番目の要素(中身)にアクセスする時

変数名[n番]

という書き方でアクセス可能です。この n番は添え字と呼びます。

この書き方でメモリアドレスにアクセスするときのアドレスの位置は、

アクセスする先のアドレス = ポインタ変数の指すアドレス + char型のサイズ x 添え字

となっています。

char 型の配列変数

文字列をchar 型ポインタ変数に代入して文字を扱う処理は 同様に下のように書くことができます。

 
chararray.c
#include <stdio.h>
int main(){
  char playername[]="tairyokuga 0 desu.";
  for ( int i = 0 ; playername[i] != 0 ; i++ ) {
    printf("%c", playername[i]);
  }
}

実行すると、下のような結果となります。

実行結果
tairyokuga 0 desu.

メモリも同様に確保されます。 つまり、配列変数に文字列を入れる方法の書き方である以下

chararray
char playername[]="tairyokuga 0 desu.";

こちらは、下の内容は同じ結果になります。

chararray
char *playername ="tairyokuga 0 desu.";

このようにchar 変数[]="文字列" のような書き方で、配列として確保した値を変数に格納すると、 配列の先頭のメモリアドレスが変数に格納されています。

ポインタ変数のアドレスを操作してみる

ポインタ変数に対して足し算、引き算をして、配列の添え字を指定するのと同様の処理ができます。

 
charpointer.c
#include <stdio.h>
int main(){
  char playername[]="tairyokuga 0 desu.";
  for ( int i = 0 ; *(playername + i) != 0 ; i++ ) {
    printf("%c", *(playername + i ));
  }
}

先ほどのプログラムの player[i]*(playername + i) に置き換わっていますね。 *(playername + i)playername のメモリアドレスを i のぶんずらした中身 という意味になります。

うーん、見た感じわかりにくいです。

結果は一緒なのですが、変数の i 番目の中身を見たい!という場合は、ソースコードは player[i] で記載したほうが読みやすいですね。

他の型の配列変数

int 型で配列を扱うときも、同様です。 int 型の配列に数値を代入して、その中身とメモリアドレスを確認してみましょう。

 
intarray.c
#include <stdio.h>
int main(){
  int money[] = {10, 5, 2222};
  for ( int i = 0 ; i < 3 ; i++ ) {
    printf("Lv %d ダンジョンクリアで %d ゴールド獲得しました。\n", i, money[i]);
    printf("money[%d] のメモリアドレスは %d です。\n", i, &money[i]);
  }
}

実行すると、下のような結果となります。

実行結果
Lv 0 ダンジョンクリアで 10 ゴールド獲得しました。
money[0] のメモリアドレスは 6684176 です。
Lv 1 ダンジョンクリアで 5 ゴールド獲得しました。
money[1] のメモリアドレスは 6684180 です。
Lv 2 ダンジョンクリアで 2222 ゴールド獲得しました。
money[2] のメモリアドレスは 6684184 です。

ソースコードを見ていきましょう!

3行目で int型の配列の宣言と初期化を行っています。

intarray.c
  int money[] = {10, 5, 2222};

char 型の配列を初期化する時は、同時に文字列を代入するとき、char playername[]="文字列" という書き方で文字の一つ一つを playername の添え字に代入できました。

char 型以外の配列へ値を初期化で入力する時は、書き方が異なります。

例えば int 型の配列、 money 変数に数字を 3 つ格納するケースです。

int money[] = {10, 5, 2222};

型 変数名[] = { 要素1, 要素2, 要素3, …以下略… }

のように変数の宣言と初期化をすることができます。 以降、変数名[n] で要素 n 番にアクセスができます。

変数の宣言と値の入力を分けると、下のように書けます。

int money[3];
money[0] = 10;
money[1] = 5;
money[2] = 2222;

次に実行結果です!

0 番目の要素(添え字)のメモリアドレス、 1 番目の要素、2番名の要素で メモリアドレスの位置を比べてみると、1つ添え字が増えると、メモリアドレスは 4 づつ増えていっていますね。

money の添え字 メモリアドレス メモリに入っている値
[0] 6684176 10
[1] 6684180 5
[2] 6684184 2222

int型のデータ1つにつき、4バイトのメモリを使っているため、4バイトづつメモリアドレスもずれています。

配列の中身はそれぞれ以下のようにメモリを使っていることになります。

  • money[0] は 6684176~6684179 の 4 バイトを使って数値(10)を保存している
  • money[1] は 6684180~6684183 の 4 バイトを使って数値(5)を保存している
  • money[2] は 6684184~6684187 の 4 バイトを使って数値(2222)を保存している

メモリのアドレスの式は下でした。ちゃんとあってますね!

アクセスする先のメモリアドレス = ポインタ変数の指すアドレス + int型のサイズ x 添え字

例えば、 money[1] でしたら、

メモリアドレス = 6684176 + 4 × 1

ですので、6684180 のアドレスにアクセスしている、ということがわかります。

ポインタ変数のアドレスを操作してみる

int型の配列についてもchar型配列と同様、 ポインタに対して足し算、引き算をして、配列の添え字を指定するのと同様の処理ができます。

 
intpointer.c
#include <stdio.h>
int main(){
  int money[] = {10, 5, 2222};
  for ( int i = 0 ; i < 3 ; i++ ) {
    printf("Lv %d のダンジョンクリアで %d ゴールド獲得しました。\n", i, *(money + i));
    printf("(money + %d) のメモリアドレスは %d です。\n", i, (money + i));
  }
}

int money[3] = { 10 ,5 , 2222};

で初期化していた場合、

*(money + 1) のメモリアドレスは、 money のポインタが指すメモリアドレスに +1 バイトした アドレスを指しているわけではなく、int型1個ぶんのメモリ分(4バイト分)がプラスになったメモリアドレスとなることに注意です。

まとめ

ポインタ変数は メモリアドレスを格納する事ができますが、 このポインタ変数への足し算引き算をすると、自由にメモリアドレスを指定してアクセスができます。

あるメモリの領域を一律チェックするときなどに使う他、メモリの構造を理解しないと、うまく使えない関数などもあります。

今回はここまでです!おつかれさまでした。

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

ページの更新履歴

更新日 更新内容
更新なし
イチからゲーム作りで覚えるC言語
第2章37 char型ポインタ変数と文字列の操作 : PREV
NEXT : 第2章39 ヌルポインタ(NullPointer) と Void について :
 
 
送信しました!

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

なんかエラーでした

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

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

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

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

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

第1章01 Visual Studio Community 2019 のインストール手順

第1章01 Visual Studio Community 2019 のインストール手順

#C11仕様#C言語#ゲームプログラミング✎ 2021-08-08
C言語でプログラミングをするために、無料で使える Visual Studio Community を使った開発環境を揃えていく手順や注意点をお話しています。
目次
第2章38 ポインタ変数への足し算
第2章38 ポインタ変数への足し算
この記事でやること
この記事でやること
配列とメモリアドレスの操作
配列とメモリアドレスの操作
char 型のポインタ
char 型のポインタ
char 型の配列変数
char 型の配列変数
ポインタ変数のアドレスを操作してみる
ポインタ変数のアドレスを操作してみる
他の型の配列変数
他の型の配列変数
ポインタ変数のアドレスを操作してみる
ポインタ変数のアドレスを操作してみる
まとめ
まとめ
非常に参考になったサイトさまや、参考文献など
非常に参考になったサイトさまや、参考文献など
ページの更新履歴
ページの更新履歴
Nodachisoft © 2021