何故、関数の引数にダブルポインタを使うのか、調べてみた…！

# 本記事について
C言語のプログラミングしていると、どうしても「何故ダブルポインタを使用しなくてはいけないのか？」「関数の引数にダブルポインタを使用しなくてはいけない理由は？」といった疑問が出てくるので、一旦整理してみた。

# 実行環境　
・macOS Ventura 13.0.1
・Homebrew GCC 12.2.0
・lldb-1400.0.38.13
・MacBook Pro 13インチ M2

# サンプルソース
下記のサンプルソースをもとに、LLDBデバッガでデバッグしてみた。
```c
  1 #include <stdio.h>
  2 
  3 static int data = 1000;
  4 
  5 void getPtr(int **ptr){
  6         *ptr = &data;
  7 }
  8 
  9 int main(void){
 10 
 11         int *p;
 12 
 13         getPtr(&p);
 14 
 15         printf("%d\n", *p);
 16 
 17         return 0;
 18 }
```
# デバッグ結果　
まずは、下記のように関数main()および関数getPtr()にブレイクポイントを仕掛けて、実行してみた。 
```c
(lldb) br set -n main
Breakpoint 1: where = main`main + 12 at main.c:13:2, address = 0x0000000100003f44
(lldb) br set -n getPtr
Breakpoint 2: where = main`getPtr + 8 at main.c:6:7, address = 0x0000000100003f1c
(lldb) r
```
すると、関数getPtr()が実行される直前でストップしたので、ここで関数main()内の変数pとグローバル型変数dataの中身とアドレス値（＝メモリのどの番地に割り当てられているか）を調べてみた。

```c
Process 21984 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003f44 main`main at main.c:13:2
   10  	
   11  		int *p;
   12  	
-> 13  		getPtr(&p);
   14  	
   15  		printf("%d\n", *p);
   16  	
Target 0: (main) stopped.
(lldb) po &p
0x000000016fdff4b8

(lldb) po p
0xc206800189d5fe3c

(lldb) po &data
0x0000000100008000

(lldb) po data
1000
```
表にすると、こんな感じ。　
グローバルint型変数dataには0x0000000100008000番地のメモリが、　
関数main()内のローカルint型変数pには0x000000016fdff4b8番地のメモリが割り当てられているのがわかります。
|変数|     アドレス値     |そのアドレス値に格納されている値|
|:-:|:----------------:|:--:|
|data|0x0000000100008000|1000| 
|p|0x000000016fdff4a8|0xf7528001a8f07e3c|

このまま、「c(=Continue)」を入力すると、下記の箇所でストップします。　
ここで関数getPtr()内のローカルint型引数ptrの中身とアドレス値を調べてみた。

```c
(lldb) c
Process 1389 resuming
Process 1389 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
    frame #0: 0x0000000100003f1c main`getPtr(ptr=0x000000016fdff4a8) at main.c:6:7
   3   	static int data = 1000;
   4   	
   5   	void getPtr(int **ptr){
-> 6   		*ptr = &data;
   7   	}
   8   	
   9   	int main(void){
Target 0: (main) stopped.
(lldb) po &ptr
0x000000016fdff478

(lldb) po ptr
0x000000016fdff4a8

(lldb) po *ptr
0xf7528001a8f07e3c
```
表にすると、こんな感じ。
こうしてみると、<font color="Blue">関数getPtr()の引数ptrに関数main()内の変数pのアドレス値が入っている</font>ことがわかります。

|変数|     アドレス値     |そのアドレス値に格納されている値|
|:-:|:----------------:|:--:|
|ptr|0x000000016fdff478|0x000000016fdff4a8<br>(<font color="Blue">=関数main()内の変数pのアドレス値</font>)| 
|*ptr<br>(<font color="Blue">=関数main()内の変数p</font>)|0x000000016fdff4a8<br>(<font color="Blue">=関数main()内の変数pのアドレス値</font>)|0xf7528001a8f07e3c|

このまま、「n(=Next)」を入力すると、下記の箇所でストップします。
再度、引数ptrの中身を確認してみると…

```c
(lldb) n
Process 1389 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100003f2c main`getPtr(ptr=0x000000016fdff4a8) at main.c:7:1
   4   	
   5   	void getPtr(int **ptr){
   6   		*ptr = &data;
-> 7   	}
   8   	
   9   	int main(void){
   10  	
Target 0: (main) stopped.
(lldb) po ptr
0x000000016fdff4a8

(lldb) po *ptr
0x0000000100008000

(lldb) po **ptr
1000
```

<font color="Red">ポインタ変数\*ptrにグローバル変数dataのアドレス値が入っている</font>ことがわかります。　
これにより<font color="Red">**ダブルポインタ変数\*\*ptrとグローバル変数dataのアドレス値が同一になった**</font>ことがデバッグ上でもわかります。


|変数|     アドレス値     |そのアドレス値に格納されている値|
|:-:|:----------------:|:--:|
|ptr|0x000000016fdff478|0x000000016fdff4a8<br>(<font color="Blue">=関数main()内の変数pのアドレス値</font>)| 
|*ptr<br>(<font color="Blue">=関数main()内の変数p</font>)|0x000000016fdff4a8<br>(<font color="Blue">=関数main()内の変数pのアドレス値</font>)|0x0000000100008000<br>(<font color="Red">=グローバル変数dataのアドレス値</font>)|
|**ptr<br>(<font color="Blue">=関数main()内のポインタ変数*p</font>)<br>(<font color="Red">=グローバル変数data</font>)|0x0000000100008000<br>(<font color="Red">=グローバル変数dataのアドレス値</font>)|1000|

最終的に関数main()に戻った際、ポインタ変数pは以下のように変わっております。　
```c
(lldb) n
Process 1389 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100003f4c main`main at main.c:15:2
   12  	
   13  		getPtr(&p);
   14  	
-> 15  		printf("%d\n", *p);
   16  	
   17  		return 0;
   18  	}
Target 0: (main) stopped.
(lldb) po &p
0x000000016fdff4a8

(lldb) po p
0x0000000100008000

(lldb) po *p
1000
```
|変数|     アドレス値     |そのアドレス値に格納されている値|
|:-:|:----------------:|:--:|
|data|0x0000000100008000|1000| 
|p<br>(<font color="Blue">=関数main()内の変数p</font>)|0x000000016fdff4a8<br>(<font color="Blue">=関数main()内の変数pのアドレス値</font>)|0x0000000100008000<br>(<font color="Red">=グローバル変数dataのアドレス値</font>)|
|\*p<br>(<font color="Blue">=関数main()内のポインタ変数*p</font>)<br>(<font color="Red">=グローバル変数data</font>)|0x0000000100008000<br>(<font color="Red">=グローバル変数dataのアドレス値</font>)|1000|

# よく間違えるパターン（ダブルポインタを使用しないケース）
ちなみに、下記のようなダブルポインタを使用しないソースでもデバッグしてみた。
```c
  1 #include <stdio.h>
  2 
  3 static int data = 1000;
  4 
  5 void getPtr(int *ptr){
  6         ptr = &data;
  7 }
  8 
  9 int main(void){
 10 
 11         int *p;
 12 
 13         getPtr(p);
 14 
 15         printf("%d\n", *p);
 16 
 17         return 0;
 18 }
```
lldbデバッガでデバッグしてみると、こんな感じ。　
```c
(lldb) br set -n main
Breakpoint 1: where = main`main + 12 at main.c:13:2, address = 0x0000000100003f44
(lldb) br set -n getPtr
Breakpoint 2: where = main`getPtr + 8 at main.c:6:6, address = 0x0000000100003f20
(lldb) 
Breakpoint 3: where = main`getPtr + 8 at main.c:6:6, address = 0x0000000100003f20
(lldb) r
Process 1324 launched: '/Users/satoshi/CProgram/test20221108/main' (arm64)
Process 1324 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003f44 main`main at main.c:13:2
   10  	
   11  		int *p;
   12  	
-> 13  		getPtr(p);
   14  	
   15  		printf("%d\n", *p);
   16  	
Target 0: (main) stopped.
(lldb) po &p
0x000000016fdff4b8

(lldb) po p
0x77220001aac6fe3c

(lldb) po &data
0x0000000100008000

(lldb) po data
1000
```

|変数|     アドレス値     |そのアドレス値に格納されている値|
|:-:|:----------------:|:--:|
|data|0x0000000100008000|1000| 
|p|0x000000016fdff4b8|0x77220001aac6fe3c|

このまま、関数getPtr()内の下記の箇所まで処理を進めると、<font color="Blue">関数getPtr()の引数ptrに関数main()内の変数pの中身が入っている</font>ことがわかります
```c
(lldb) c
Process 1324 resuming
Process 1324 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 3.1
    frame #0: 0x0000000100003f20 main`getPtr(ptr=0x77220001aac6fe3c) at main.c:6:6
   3   	static int data = 1000;
   4   	
   5   	void getPtr(int *ptr){
-> 6   		ptr = &data;
   7   	}
   8   	
   9   	int main(void){
Target 0: (main) stopped.
(lldb) po &ptr
0x000000016fdff488

(lldb) po ptr
0x77220001aac6fe3c
```

|変数|     アドレス値     |そのアドレス値に格納されている値|
|:-:|:----------------:|:--:|
|ptr|0x000000016fdff488|0x77220001aac6fe3c<br>(<font color="Blue">=関数main()内の変数pの中身</font>)| 

このまま、「n(=Next)」を入力すると、下記の箇所でストップします。
再度、引数ptrの中身を確認してみると…
```c
(lldb) n
Process 1324 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100003f2c main`getPtr(ptr=0x0000000100008000) at main.c:7:1
   4   	
   5   	void getPtr(int *ptr){
   6   		ptr = &data;
-> 7   	}
   8   	
   9   	int main(void){
   10  	
Target 0: (main) stopped.
(lldb) po ptr
0x0000000100008000

(lldb) po *ptr
1000
```
<font color="Red">関数getPtr()内の変数ptrの値がグローバル変数dataのアドレス値が変わっている</font>ことがわかります。　
ただこれだと、<font color="Red">**関数main()内のポインタ変数\*ptrにグローバル変数dataのアドレス値を割り当てられていない**</font>ことがデバッグ上でもわかります。


|変数|     アドレス値     |そのアドレス値に格納されている値|
|:-:|:----------------:|:--:|
|ptr|0x000000016fdff488|0x0000000100008000<br>(<font color="Red">=グローバル変数dataのアドレス値</font>)| 
|*ptr<br>(<font color="Red">=グローバル変数data</font>)|0x0000000100008000<br>(<font color="Red">=グローバル変数dataのアドレス値</font>)|1000|

```c
(lldb) n
Process 1324 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100003f4c main`main at main.c:15:2
   12  	
   13  		getPtr(p);
   14  	
-> 15  		printf("%d\n", *p);
   16  	
   17  		return 0;
   18  	}
Target 0: (main) stopped.
(lldb) po p
0x77220001aac6fe3c
```
|変数|     アドレス値     |そのアドレス値に格納されている値|
|:-:|:----------------:|:--:|
|data|0x0000000100008000|1000| 
|p|0x000000016fdff4b8|0x77220001aac6fe3c|

<font color="Red">**↑これだと、関数main()内のポインタ変数pの中身を変えられない…**</font>

# 結論　
ダブルポインタを使用する最大の要因は、やはりコレですね… 
<font color="Red">**呼び元の関数内のポインタ変数に割り当てるアドレスを変えたい場合は、ダブルポインタを使うべし！！**</font>
っちゅうことですね…