初心者がポインタをテキトーに考える[C言語]


#はじめに
C言語初心者ながら他人にポインタを教える場面がありましたが、うまく説明できませんでした…
なのであるかわからない次の機会のために自分なりにC言語のポインタについて初歩の初歩から考えていきます。
訂正・誤りや付加知識等あれば言っていただけると助かります。

#本題の前に…
##「pointer」の和訳
なんとなく解ると思いますが**「(何かしらを)指すもの、指す人」**というような意味です。
![pointer.jpg](https://qiita-image-store.s3.amazonaws.com/0/264254/a7f83214-10d5-6f03-6904-eb7785cd9752.jpeg)

では、C言語における「ポインタ」は何を指しているのでしょうか？

##メモリとかいう表
ポインタが何を指しているか気になるところではありますが、そのまえにメモリについて軽く触れておきましょう。
コンピュータの中にはメモリと呼ばれる**アドレスと数値データのセット**がズラッとならんだ**大きな表**があります。
![table_normal.jpg](https://qiita-image-store.s3.amazonaws.com/0/264254/ac8deb3c-3396-b1ec-e145-5c0b61b2a144.jpeg)

<details><summary>**他の何かに例えると…？**</summary>
<div>

[アドレスとデータ]の関係は[住所と住宅]だったり[出席番号と生徒]の関係などに例えられたりします。
ですがここでは、**アドレスをロッカーの番号**、**データをロッカーの中身**に置き換えて考えていきます。
さっきの表みたいに書くとこんな感じになります。
![rocker_normal.jpg](https://qiita-image-store.s3.amazonaws.com/0/264254/2f2f6985-8b27-5bc4-1f07-07905630c84e.jpeg)

ロッカーの番号が分かると、その番号のロッカーの中身が分かります。
同じように、アドレスが分かると、そのアドレスにある数値データが分かります。
<details><summary>その逆は必ずしも成り立つとは言えません。</summary>
<div>例えばロッカーの中身が靴だとわかっても1番のロッカーか1001番のロッカーかわかりません.</div></details>

</div></details>

##ポインタは何を「指す」のか
一回間を置きましたが、C言語における「ポインタ」は何を「指す」のでしょうか？
ポインタは**アドレスを指します**。
もっと言うとポインタはアドレスを指すことで、その場所にあるデータについて操作できるようになります。

例えばさっきの表において、ポインタが1001を指す(保持している)とデータは23であることがわかったり、違う数値データに変更したりできます。
![table_pointer.jpg](https://qiita-image-store.s3.amazonaws.com/0/264254/153444a2-85ac-5558-1446-863f1b330207.jpeg)


<details>
<summary>**他の何かに例えると…？**</summary>
<div>
先ほどと同じようにロッカーに置き換えて考えると、ポインタはロッカーの鍵と例えられるでしょうか。
例えばロッカーの鍵が1001番のものであれば、その中身(靴)を見ることもできますし、それを取っちゃうこともできます。
もちろん空いた1001番のロッカーに別の何かを入れることもできます。
つまり、鍵を手にすることで1001番ロッカーを自由にすることができるのです。
![rocker_pointer.jpg](https://qiita-image-store.s3.amazonaws.com/0/264254/9635b012-2cef-f0e7-5e8a-7e16f1eb399f.jpeg)

</div>
</details>

#ポインタを利用する時に出てくるよくわからん記号
##記号たちの投げやりな説明
<dl>
<dt>謎の記号1つ目 : & (アンド)</dt>
<dd>たぶん参考書などを最初から読み進めている人はscanf関数でお世話になっている謎の記号。</dd>
<dd>変数の前につけると、そのデータが格納されている場所(アドレス)を教えてくれます。</dd>
</dl>

<details>
<summary>さっきの表に戻って考えてみると…</summary>
<div>

![table_and.jpg](https://qiita-image-store.s3.amazonaws.com/0/264254/cd83d287-b9fd-90df-f158-34e1af7f3b90.jpeg)
&を変数の前につけることでそのアドレスが得られますが、**アドレスを変更することはできません**。
</div>
</details>

<dl>
<dt>謎の記号2つ目 : * (アスタリスク)</dt>
<dd>乗算の演算子がいきなりわけのわからないタイミングで登場。</dd>
<dd>ポインタを利用するときは以下の二つの機能を持っていると考えれば大丈夫だと思います。</dd>
<dd>
 <dl>
 <dt>機能-1 : この変数をポインタとして使います<dt><dd>変数の宣言時に付随させることで、お前(変数)はポインタだ！ってできます。強い。</dd>
 <dd>(例) int *pointer;  　char *poiner; 　　など</dd>
 <p> </p>
 <dt>機能-2 : このアドレスにあるデータを操作します<dd>アドレスの前につくと、その場所をいじる権利を手に入れられます。権力強すぎない??</dd><dd>(例) *pointer;</dd>
 </dl>
</dd>
</dl>

##記号を実際に使ってみる
説明はこのあたりにしておいて、実際に使ってみましょう。
###& (アンド)を使ってみる
以下のコードを入力してみてください。

```c:sample_and.c
int hoge=10;
printf("%p\n",&hoge);
```
どんな値が出力されましたか？
おそらく0xから始まる数値が出たと思います。この0xは16進数であることを示します。
これはint型変数hogeが格納されているアドレスを示しています。

###* (アスタリスク)を使ってみる
まず機能-1と機能-2を合わせて見ていきましょう。

```c:sample_ast_1.c
int hoge=10;
int *p;                  //機能-1,pをポインタとして宣言する
p = &hoge;               //pにアドレスをセットする
printf("1 -> %d\n",*p);  //機能-2,アドレスpの場所を操作(データを取得)する
*p = 20;                 //機能-2,アドレスpの場所を操作(データを代入)する
printf("2 -> %d\n",*p);
```
出力は以下のようになります。

```:output
1 -> 10
2 -> 20
```
これを表にして考えてみましょう。
![table_ast_1.jpg](https://qiita-image-store.s3.amazonaws.com/0/264254/e616da2e-99e9-7d18-8a91-8204e5e08d7e.jpeg)
図中にある丸付きの数字の順に見ていきましょう。

1. `int hoge=10;` -> 変数`hoge`が宣言され、任意のアドレスを占有します。今回は仮に1001番地を占有したとします。
2. `&hoge` -> `&`の機能で変数`hoge`のアドレスを取得。
3. `p = &hoge;` -> 取得したアドレス(&hoge)をポインタにセット。
4. `*p` -> ポインタの示すアドレス(&hoge)の場所にあるデータを取得。
5. `*p = 20;` -> ポインタの示すアドレス(&hoge)の場所にデータ(20)を代入。
といった感じです。

今度は機能-2の方を特に見ていきます。

```c:sample_ast_2.c
int hoge=10;
printf("hoge's address : %p\n",&hoge);
printf("hoge is '%d'\n",*(&hoge));
```
自分の環境では出力は以下のようになりました。

```:output
hoge's address : 0x7fff31658004
hoge is '10'
```

このコードで新しい部分は`*(&hoge)`の部分です。
先程、アスタリスクには「アドレスの前につくと、その場所のデータを操作できる」機能があると言いました。
そのため`&hoge`は変数hogeのアドレスを示すため、その先頭にアスタリスクをつけると「変数hogeのデータを操作できる」ようになります。

#ポインタの使い方
##ポインタ型変数の宣言
宣言するときは、`データ型 *変数名`または`データ型* 変数名`という形を取ります。
複数の変数を宣言するときは`データ型* 変数名'の方は型がわかりずらくなるので注意が必要です。

```c:declare_pointer.c
int *a;     //ポインタ
int* b;     //これもポインタ
int *c1,c2; //c1はポインタだが、c2はポインタではない
int* d1,d2; //d1はポインタだが、d2はポインタではない
```

##ポインタをいじる
宣言したポインタにアドレスを入れてみましょう。
ここでやることは普通の変数と大差ありません。

```c:ctrl_pointer.c
int a=10;
int *p;
p = &a;
```
以上です。ですがこれだけだとアドレスが入ったただの箱です。(まぁ変数はそんなものですが…)
ちゃんと「指し示す」機能を使いましょう。その機能を実現するのが「*」です。これを先頭につけて、

```c:ctrl_pointer.c
int a=10;
int *p;
p=&a;
*p=20;      //*を先頭につける
printf("*p is '%d'\n",*p);
printf(" a is '%d'\n", a);
```

```:output
*p is '20'
 a is '20'
```
「*」の権力を活用してアドレスpの中身を20に変えただけなのに、変数aの値も20になっています。
ここでどこかで貼ったような画像を、もう一回貼ります。
![table_usage_1.jpg](https://qiita-image-store.s3.amazonaws.com/0/264254/2768f495-632e-5173-5735-39c4fdf743fb.jpeg)

この5番の動作が終了するとどうなるでしょうか？

![table_usage_2.jpg](https://qiita-image-store.s3.amazonaws.com/0/264254/ef673886-9979-d266-250e-a2e20a4f6a9c.jpeg)
ってことです。

#ポインタと〇〇
<!-- ********************************************* ポインタと引数 -->
##ポインタと引数
まず普通の変数を引数に取る関数の動作を見ていきます。

```c:val.c
#include<stdio.h>

void func(int arg){
  arg = 777;
  printf("arg in func is '%d'\n", arg);
}

int main(void){
  int a=0;

  int val = a;
  func(val);
  printf("val is '%d'\n",val);

  return 0;
}
```

```:output
arg in func is '777'
val is '0'
```
valとargの値の変遷としては以下のようになっています。
valは終始変わらず、argは孤立して関数内で変化しています。
また表における変遷のイメージはこんな感じです。
(画像作成中)


<!-- 
＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊画像作る＊＊＊＊＊＊＊＊＊＊＊＊＊
-->


次に引数にポインタ型の変数を取る関数の動作を見ていきましょう。

```c:ref.c
#include<stdio.h>

void func(int *arg){
  *arg = 777;
  printf("*arg in func is '%d'\n", *arg);
}

int main(void){
  int a=0;

  int *ref = &a;
  func(ref);
  printf("ref is '%d'\n",*ref);

  return 0;
}
```

```:ans_3
*arg in func is '777'
ref is '777'
```
valとargの値の変遷としては以下のようになっています。
先ほどと異なり、valは関数func内のargと同じタイミングで同じ変化をしています。
また表における変遷のイメージはこんな感じです。
(画像作成中)

<!--
＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊表をいれます＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
-->

<!-- ********************************************* ポインタと配列 -->
##ポインタと配列
配列は`int array[]={0,1,2};`のように配列を宣言して、`array[0]`というように`[]`を利用して配列の各要素にアクセスします。
ではこの`array`は何なのでしょうか？ `[]`を取っ払ってみましょう。

```c:what_is_array.c
int array[]={0,1,2};
printf("array is '%p'\n", array);
```

```:output
array is '0x7ffeba62f4d0'
```
<font color="lightgray">変換指定子でバレバレですが、</font>arrayはアドレスを示しています。

ということで(?)、`*`や`&`、さらには配列でいつも使っている`[]`を使っていろいろしてみます。

```c:play_array.c
char value='a';
char array[]={'0','1','2','3','4','5'};
char *p_char;

printf("1. *array    -> '%c'\n",*array);

p_char = &value;
printf("2. p_char[0] -> '%c'\n", p_char[0]);

p_char = array;
printf("3. p_char[2] -> '%c'\n", p_char[2]);
```

```:output
1. *array    -> '0'
2. p_char[0] -> 'a'
3. p_char[2] -> '2'
```
いろいろ出てきました。順番に見ていきましょう。
1つ目、`array`に`*`を付けたところ、`0`が得られました。つまり`*`の機能によって、`array`の指すアドレスの場所にある値`0`が得られたということです。`0`が配列の先頭要素であるので、どうやら`array`は配列の先頭のアドレスを示しているようです。

配列とアドレスの関係を表の形で見てみましょう。
(画像作成中…)

<!--
＊＊＊＊＊＊＊＊＊＊＊そのうち表を作成します。＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
-->

<!-- ********************************************* ポインタと文字列 -->
##ポインタと文字列
とりあえずコードを見てみましょう。

```c:ctrl_pointer.c
printf("\"str1\" is '%p'\n","str1");
printf("\"str2\" is '%p'\n","str2");
```

```:output
"str1" is '0x4005e4'
"str2" is '0x4005f9'
```
いつも出力するときになんとなく使っていた`""`ですが、どうやらこれもアドレスを返してくるみたいです。
`""`の機能としては各文字を文字コードに変換し、char型の配列に入れたあと、最後尾に`0`を付け足します。
この`0`は文字に直すと`\n`で、NULL文字と言われます。文字列の終わりを示す文字ですね。
そして最後に作成した配列の先頭アドレスを戻してくる。といった感じになってます。
`""`の動作をメモリの表で見るとこんな感じです。
(画像作成中…)

<!-- 
＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊画像を作成し次第、添付します。＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
--->

#データ型をつける理由

```c:ctrl_pointer.c
char val_char;
char *p_char=&val_char;
printf("[p_char is %p] -> [p_char+1 is %p]\n", p_char, p_char+1);

int val_int;
int *p_int=&val_int;
printf("[p_int  is %p] -> [p_int+1  is %p]\n", p_int, p_int+1);
```

```:output
[p_char is 0x7fffd35b6113] -> [p_char+1 is 0x7fffd35b6114]
[p_int  is 0x7fffd35b6114] -> [p_int+1  is 0x7fffd35b6118]
```
この出力で見て欲しいのは左右のアドレスの差です。
char型の方では113から**1増えて**114になっています。しかし、int型の方では114から**4増えて**118になっています。
(以下随時追記します。)

<!--
ロッカーに置き換えて考えてみましょう。小さいサイズのロッカーは大量に用意できましが、それ以外のサイズのものがなかったとします。
＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊表を作成する＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
ここで大きい荷物がいくつか来た場合、どうしますか？ 小さいロッカーを4つくらい連結して大きくしちゃいましょう。
＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊表を作成する＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
この時のロッカーの番号はどのようになるでしょうか？ 見てみましょう。
＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊表を作成する。＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
このように…
またデータ型がないと`[]`をつけるときにも困ります。どの程度アドレスが離れているかわからないため、飛ぶ距離を決定できないからです＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊表を作成する。＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
-->

#問題 : 出力はどうなる？？
以下にコードを示すのでその出力を考えてみてください。

<!--  **************************************************@ques1  -->

##問題1 *と&の機能 -1-

```c:ques_1.c
#include<stdio.h>

int main(void){
  int a=20;
  int *p = &a;
  printf("%d\n",5*(*p));

  return 0;
}
```
<details>
<summary>出力結果はこちら</summary>
<div>

```:ans_1
100
```
</div>

<!--  **************************************************@ques2  -->

</details>
##問題2 *と&の機能 -2-

```c:ques_2.c
#include<stdio.h>

int main(void){
  int a=20;
  printf("%d\n",(*&a)/4);

  return 0;
}
```
<details>
<summary>出力結果はこちら</summary>
<div>

```:ans_2
5
```
</div>
</details>

<!--  **************************************************@ques3  -->

##問題3 ポインタと引数

```c:ques_3.c
#include<stdio.h>

void arg_ref(int *arg){
  *arg = 10;
}

void arg_val(int arg){
  arg = 100;
}

int main(void){
  int a;
  
  a=1;
  int *ref = &a;
  arg_ref(ref);
  printf("a is '%3d', *ref is '%3d'\n", a, *ref);

  a=1;
  int val = a;
  arg_val(val);
  printf("a is '%3d',  val is '%3d'\n", a, val);
  return 0;
}
```
<details>
<summary>出力結果はこちら</summary>
<div>

```:ans_3
a is ' 10', *ref is ' 10'
a is '  1',  val is '  1'
```
</div>
</details>

<!--  **************************************************@ques4  -->

##問題4 ポインタと配列

```c:ques_4.c
#include<stdio.h>

int main(void){
  int array[]={0,10,20,30,40,50};
  int *p=array;
  
  printf("*(array+2)   -> %d\n", *(array+2));
  printf("(array+1)[2] -> %d\n", (array+1)[2]);
  printf("p[5]         -> %d\n", p[5]);
  printf("(p+1)[0]     -> %d\n", (p+1)[0]);
 
  return 0;
}
```
<details>
<summary>出力結果はこちら</summary>
<div>

```:ans_4
*(array+2)   -> 20
(array+1)[2] -> 30
p[5]         -> 50
(p+1)[0]     -> 10
```
</div>
</details>

<!--  **************************************************@ques5  -->

##問題5 ポインタと文字列

```c:ques_5.c
#include<stdio.h>

int main(void){
  char *p=(char *)"hoge";
  printf("%c\n",p[2]);
  printf(p);     putchar('\n');
  printf(&p[1]); putchar('\n');

  printf("%s\n","fuga");
  printf("%s\n",&"fuga"[2]);

  return 0;
}
```
<details>
<summary>出力結果はこちら</summary>
<div>

```:ans_5
g
hoge
oge
fuga
ga
```
</div>
</details>

<!--  **************************************************@ques6  -->

</details>
##問題6 ポインタとデータ型

```c:ques_6.c
#include<stdio.h>

int main(void){
  char *p_char = (char *)0x7FFF8000;
  int  *p_int  =  (int *)0x7FFF8000;

  printf("p_char   is '%p'\n", p_char);
  printf("p_char+1 is '%p'\n", p_char+1);

  printf("p_int    is '%p'\n", p_int);
  printf("p_int+1  is '%p'\n", p_int+1);

  return 0;
}
```
<details>
<summary>出力結果はこちら</summary>
<div>

```:ans_6
p_char   is '0x7fff8000'
p_char+1 is '0x7fff8001'
p_int    is '0x7fff8000'
p_int+1  is '0x7fff8004'
```
</div>
</details>