Cのポインタとアセンブリの対応関係、およびポインタを使うメリット

### 概要

以下の記事の要点だけを、

1. Cコードとアセンブリ命令を1対1で対応させて書き直し、
2. ポインタを使うメリットをちょっと補足した記事です。

[アセンブリ言語のさわりを知ればC言語のポインタもわかるよね、という話 \- Qiita](https://qiita.com/atworks/items/17da1973378a626e6467)

ちなみに以下のアセンブリはARMのCPUのものですが、x86-64でもやっていることは同じです。

- `fp`は「フレームポインタ」
  - フレームポインタはメモリ領域の中の**決まった場所**を指していて、例えばリターンアドレスが格納されている位置を指している。
- `r3`は「汎用レジスタ」

あと、読みやすさのためにアセンブリコードの`#`は削除しました。

### サンプルコード

```c:ポインタなし
void main() {
    int a;
    int b;

    b = 1;
    a = b;

    a = 2;
}
```

```C:ポインタあり
void main() {
    int *a;
    int b;

    b = 1;
    a = &b;

    *a = 2;
}
```

### アセンブリを使った解説

#### ポインタなし

```c
    int a;
    int b;
```

`a`も`b`も`int`なので、4バイトずつ離れたアドレス（`a`: -8番地、`b`: -12番地）を割り付ける（あとでコンパイラが）

```c
    b = 1;
```

```
mov     r3, 1
str     r3, [fp, -8]
```

1. 数字の**1**をレジスタ`r3`にコピー`mov`します。
2. `r3`にコピーした**値**(**1**)を、メモリの-8番地（**b**）に代入`str`します。

|-8番地|-12番地|
|------|------|
|     1|      |

```c
    a = b;
```

```
ldr     r3, [fp, -8]
str     r3, [fp, -12]
```

1. さっき-8番地(**b**)に代入した値を、もう一回レジスタ`r3`に読みとります（`r3`には値(**1**)が入る）。
2. レジスタ`r3`にコピーした**値**(**1**)を、-12番地（**a**）に代入`str`します。

|-8番地|-12番地|
|------|------|
|     1|     1|

```c
    a = 2;
```

|-8番地|-12番地|
|------|------|
|     2|    1|

#### ポインタあり

```c
    int *a;
    int b;
```

1. `a`は`int`を指すポインタ（32ビットアドレッシングの処理系であれば「ポインタのサイズは4バイト」なので結果的に-4としてオフセット）
2. `b`は`int`
3. なので、4バイトずつ離れたアドレス（`a`: -12番地、`b`: -8番地）を割り付ける
4. （あとでコンパイラが）

```c
    b = 1;
```

```
mov     r3, 1
str     r3, [fp, -12]
```

1. **値**（**1**）をレジスタ`r3`にコピー`mov`します。
2. `r3`にコピーした**値(1)**を、メモリの-12番地（**b**）に代入`str`します。

|-8番地|-12番地|
|------|------|
|      |     1|

```c
    a = &b;
```

```
sub     r3, fp, 12
str     r3, [fp, -8]
```

`b`のアドレス`&b`（-12番地）をaに代入します。

1. 基準アドレス（`fp`）から**12**を引いた`sub`アドレス（**-12番地**）をレジスタ`r3`に代入します（`r3`にはアドレスが入る）。
2. さっきレジスタ`r3`にコピーしたアドレス(**-12番地**)を-8番地（**\*a**）に代入します。

|-8番地|-12番地|
|------|------|
|-12番地|    1|

```c
    *a = 2;
```

`*a`は-12番地の値なので、`b=2`になる

|-8番地|-12番地|
|------|------|
|-12番地|    2|

### 結論

ポインタは以下のようなときに使える

#### 文字列操作

このように間接的に参照できるので、ポインタを使って以下のように文字列の文字を書き換えることができます。

```c
char str[] = "hello";

for (char* p = str; *p != '\0'; p++) {  // 1番地ずつ見ていく
    if (*p == 'l') {  // 'l'を探して
        *p = '*';  // '*'に書き換える
    }
}
```

char型のアドレスのオフセットは1なので、文字列の先頭のアドレスから舐めるように1番地ずつ順番に見ていくことができる。

||-8番地|-9番地|-10番地|-11番地|-12番地|-13番地|
|----|------|------|------|-------|------|-------|
|変更前|h|e|l|l|o|\0|
|変更前|h|e|*|*|o|\0|

#### その他

以下のような使い方もできます

##### インクリメント関数

###### ポインタを使わない場合

```c
void increment(int a)
{
  a += 1;
}

int b;
b = 1;
increment(b);
```

と呼び出しても、引数の値は関数呼び出しでコピーになるので、bは1のまま。

###### ポインタを使う場合

```c
void increment(int* a)
{
  *a += 1;
}

int b;
b = 1;
increment(&b);
```

で、func内から呼び出し元の変数を書き換えることができます。

##### 引数を1文字ずつ取り出す

例えば`$git commit`ならば、例えば以下のようにメモリに格納されます：

**argv[0] - 第0引数**

|-17番地|-18番地|-19番地|-20番地|-21番地|
|---|---|---|---|---|
|$|g|i|t|\0|

**argv[1] - 第1引数**

|-57番地|-58番地|-59番地|-60番地|-61番地|-62番地|-63番地|
|---|---|---|---|---|---|---|
|c|o|m|m|i|t|\0|

```c
    char *p = argv[1];
```

`argv[1]`は第1引数の文字列のアドレス（**\-57番地**）です。

[低レイヤを知りたい人のための Cコンパイラ作成入門](https://www.sigbus.info/compilerbook/#%E3%82%B9%E3%83%86%E3%83%83%E3%83%972%E5%8A%A0%E6%B8%9B%E7%AE%97%E3%81%AE%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9%E3%81%AE%E4%BD%9C%E6%88%90)

### 謝辞

[Kouji Matsui \(@kekyo2\)](https://twitter.com/kekyo2) さん、[matsujirushi \(@matsujirushi12\)](https://twitter.com/matsujirushi12) さん、アドバイスありがとうございました！