【Go】ポインタについてまとめてみた

## はじめに
この**記事の対象**は

- ポインタってなんとなく聞いたことあるけど詳しく説明できない
- Goを入門したばかりでポインタの扱い方が分からない

という方を対象としています。
**「Goの変数宣言ってどうするの？？」**って方は一旦そちらを学んでからの方が理解できるかと思います！

また、この記事を読んでくださって、もし「認識が違うよ！」「ここ間違ってるよ！」などがあればご教授ください！
よろしくお願いします！

## 出てくるワード
この記事に関係するワードとして、

- 変数
- メモリ
- アドレス
- ポインタ
- ポインタ型
- ポインタ変数
- ポインタ値
- デリファレンス

などが出てきます。
今分からなくても、それぞれ説明するので安心してください！
（知らないワードがいくつも出たら思考停止しちゃいますよね...。）

## 変数とメモリとアドレスの関係
### それぞれどんなものかをざっくり説明すると、

メモリ...データが入ってくる箱のイメージ。
アドレス...メモリを特定する為の住所のイメージ。メモリにデータが格納される時にメモリに付いてくる。
変数...データを格納するメモリに名前を付けるイメージ。

### もうちょっと詳細に説明すると、
```go
var num int = 100
```
上記のような変数宣言をすると、100と言うデータが、どこかの**メモリ**に保存されます。
すると、この時にそれぞれのメモリを区別する為に番号が振られます。これが**アドレス**です。
ただ、このアドレスは16進数で表されて**分かりにくい**ので**変数**と言う名前を付けて、人間に分かりやすくしています。

#### Goではどういう記法で書くか
例えばメモリを確保するということは、変数宣言すれば良いわけです。
**アドレスの参照**は、**変数の前に`&`**(アンパサンド)を付けてあげればOKです。

```go
package main

import "fmt"

func main() {
  // 変数宣言＋値代入
  var num int = 100
  // メモリに格納された値を表示
  fmt.Println(num) // => 100(メモリの値)
  // メモリのアドレスを表示
  fmt.Println(&num) // => 0xc00009a008(16進数のアドレス)
}
```
**【イメージ】**
![スクリーンショット 2021-02-16 19.50.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305767/20b87373-a957-6f01-456d-2a0561d3491f.png)
【参考】
[数値を記憶する - 苦しんで覚えるC言語](https://9cguide.appspot.com/05-01.html)
[変数とメモリの関係 - 苦しんで覚えるC言語](https://9cguide.appspot.com/15-02.html)

## ポインタとは
**ポインタ**ってなんでしょう...？
### ざっくり説明すると、
「アドレスの値を記憶する変数」のことを一般的には言うみたいです。

### もっと詳細に説明
「[アドレスを記憶する変数 - 苦しんで覚えるC言語](https://9cguide.appspot.com/15-04.html)」には下記のように書かれています。
> 世間では、アドレスの値を記憶する変数をポインタと呼んでいますが、
これは本当はあまり正確な呼び方ではありません。
何故なら、ポインタとは、アドレスを扱う機能3つの総称だからです。
ポインタという呼び方は総称であり、正確には3種類に別れています。

**ポインタとは、アドレスを扱う機能3つの総称**だそうです。
で、このポインタの3つと言うのが**ポインタ変数**・**ポインタ型**・**ポインタ値**です。

まずはこの3つワードの説明。
#### ポインタ型
int型を「intを記憶する変数」の型、と表現するなら、ポインタ型は**「アドレスを記憶する変数」の型**です。
int型やstring型などの型の一種です。

#### ポインタ変数
**ポインタ型で宣言された変数**のことを指します。int型やstring型の変数と変わりありません。
ポインタ変数が記憶できる値は**アドレス**です。
他の型の変数と同じく、別のアドレス(ポインタ値)を代入できます。
「記憶しているアドレス」のメモリの値を取得・書き換えが可能です。

#### ポインタ値
**ポインタ変数が記憶できる値（アドレス）**のことを指します。

#### Goではどういう記法で書くか
次に、このポインタに関してGoではどのような書き方をするか見てみましょう。
以下のコードはGoでのポインタの記法です。

```go
// メモリを確保(アドレス付き)
var num int = 100
fmt.Println(num) // => 100

// ポインタ変数宣言＋アドレス代入
var pointer *int = &num
fmt.Println(pointer) // => 0xc00009a008
```
`*int`がポインタ型、`pointer`がポインタ変数、`&num`がポインタ値にあたります。

`*int`（ポインタ型）の`*`はポインタ型を表す記号です。もしもstring型の値が格納されたアドレスを代入した場合は`*string`となります。
`&num`（ポインタ値）は、先ほど説明した通りアドレスのことです。この場合、`num`のアドレス。

`var pointer *int = &num`で**ポインタ変数の宣言**と**アドレス代入**を行っています。

変数`pointer`には変数`num`のアドレスを代入しているので、当然「変数`pointer`のメモリの値」と「変数`num`のアドレス」は同じ値になります。

```go
var num int = 100
fmt.Printf("value=%v type=%T", &num, num)        // => value=0xc00009a008 type=int

var pointer *int = &num
fmt.Printf("value=%v type=%T", pointer, pointer) // => value=0xc00009a008 type=*int
```

**イメージ**
![スクリーンショット 2021-02-17 12.39.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305767/1576bd20-d424-d8a1-309a-e434761c02a4.png)

#### ポインタ変数から値にアクセスする
繰り返しになりますがポインタ変数にはポインタ値、つまりアドレスが記憶されています。
しかし、この**ポインタ変数の前に`*`を付ける**とポインタ変数に記憶されている**アドレスの中身の値にアクセス**することが可能になります。

```go
var num int = 100
var pointer *int = &num

fmt.Println(num) // => 100
fmt.Println(*pointer) // => 100
```


**イメージ**
![スクリーンショット 2021-02-17 16.17.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305767/cb864168-1998-bff5-5d47-505527476685.png)

### デリファレンス
いきなりですが、ここで質問です。
以下のコードはどのような値が出力されるでしょうか？？

```go:問題
package main

import "fmt"

func main() {
  var num int = 100
  var pointer *int = &num
  *pointer = 999
  fmt.Println(num)     // => ???
  fmt.Println(*pointer) // => ???
}
```

正解は
.
.
.

```go:正解
fmt.Println(num)      // => 999
fmt.Println(*pointer) // => 999
```
どちらも「999」になります。どちらもです！

**解説**
まず変数`pointer`はポインタ変数です。
ポインタ変数にはアドレスが記憶されるんでしたね。

で、`*pointer`ですが、ポインタ変数の前に`*`を付けるとポインタ変数に記憶されているアドレスの中身の値にアクセスできると説明しました。

結局`*pointer = 999`は何をしているかと言うと、`*pointer`つまり、記憶されているアドレスの中身の値つまり、`num`のメモリの値を`999`に変更している。と言うことです。
なので、どちらも`999`になると言う訳です。
![スクリーンショット 2021-02-17 18.39.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305767/8eb3a520-e0ae-bafa-27b5-eb5c75575e28.png)


ちなみに、「ポインタ変数`*pointer`は記憶されているアドレスの中身の値にアクセスできる」と言いましたが、このことを**デリファレンス**と呼びます。
> 参照元から参照先の値を得ることを特に指す語

引用元：[間接参照 - Wikipedia](https://ja.wikipedia.org/wiki/%E9%96%93%E6%8E%A5%E5%8F%82%E7%85%A7)

## 雑談：学んでいて面白いと思った部分
### C言語（Goも）って参照渡しってのはできない
C言語（Goも）って参照渡しってのはできないんですね。

>C言語では、あくまでも値渡ししか出来ないのですが、
アドレスを渡すことを慣習的に参照渡しと呼ぶことがあります。

### `&`付き変数の正体はアドレスを求める演算子
ちなみに、`&`付き変数の正体についてはこんな記述がありました。
> この&付き変数の正体は、非常に単純です。
実は、&は変数のアドレスを求める演算子なのです。

参照：[&つけが必要な変数の正体](https://9cguide.appspot.com/15-03.html)

まあまあ`&`に関しては気になっていたので紹介しました。

以上です。

## まとめ
逃げてきたポインタの理解をする為にこの記事を書いていたのですが、調べだすとなかなか面白い領域だと感じました！
知らないことを知っていくって面白いですね〜。（しんどい時もあるけど...）

もし、認識が間違っているとか、「ここどう言うこと？」って言うのがあれば是非教えて下さい！
よろしくお願いします！

それでは、これからも一緒に良いエンジニアライフを歩みましょ〜:fist:
お疲れ様でした！:wave:

## 参考にさせて頂いたサイト
どのサイトもまとめ方が上手くて憧れます。そしてポインタの理解にとても役立ちました。
本当にありがとうございます！

[数値を記憶する - 苦しんで覚えるC言語](https://9cguide.appspot.com/05-01.html)
[変数とメモリの関係 - 苦しんで覚えるC言語](https://9cguide.appspot.com/15-02.html)
[ポインタ (pointer)とは｜「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word12815.html)
[ポインタ変数とは｜「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word12814.html)
[Goで学ぶポインタとアドレス](https://qiita.com/Sekky0905/items/447efa04a95e3fec217f)
[アドレスを記憶する変数 - 苦しんで覚えるC言語](https://9cguide.appspot.com/15-04.html)
[C言語のポインタ構文のつまづきどころ](https://qiita.com/tatachiy/items/7856931f4cacf2c5b743)
[デリファレンス (dereference)とは｜「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word12818.html)
[間接参照 - Wikipedia](https://ja.wikipedia.org/wiki/%E9%96%93%E6%8E%A5%E5%8F%82%E7%85%A7)
[&つけが必要な変数の正体 - 苦しんで覚えるC言語](https://9cguide.appspot.com/15-03.html)