ポインタでつまづいたあなたに向けたアドレス空間の世界【Go言語】

# メモリ空間なんて意識してこなかった

私はこれまでPython、JavaScript/TypeScript、PHPといった言語を中心にコーディングしてきました。しかし、仕事の都合上、Go言語の学習が必要になり、少しずつ学び始めてから2週間が経ちました。

その過程で、ポインタの概念に何度も躓いています。私のように他の言語からGoに移行し、ポインタに頭を悩ませる人は少なくないようです。実際、QiitaなどのプラットフォームではGoのポインタに関する記事がたくさん投稿されています。

ポインタに関する説明を読むと、最初は理解できた気になるものの、すぐにそのイメージが掴めなくなってしまいます。これまで高級言語のぬるま湯にどっぷり浸かっていたため、メモリに関連する話題が出ると、認知の負担が爆増し、一度理解したこともすぐに忘れてしまうのです。

そんなわけで、ポインタに限らず、メモリ空間内で何が起こっているのかをしっかりとイメージしながらGoを学んでみたところ、徐々に霧が晴れてきました。付随的にポインタ以外の多くの疑問も解決できたため、その過程の一部を備忘録としてここに記録しておきます。

:::note warn
素人が自分なりに調べてまとめた内容になりますので、不正確な記述や誤りもあるかもしれません。間違いはあたたかくご指摘いただけるととっても嬉しいです。
:::

### 目次
[メモリ空間なんて意識してこなかった](#メモリ空間なんて意識してこなかった)
[1. アドレスのイメージをつかむ](#1-アドレスのイメージをつかむ)
[2. 変数宣言はアドレスの確保である](#2-変数宣言はアドレスの確保である)
[3. ポインタはアドレスを管理する変数](#3-ポインタはアドレスを管理する変数)
[4. 配列は同じ型のデータを連続したメモリ領域に格納する](#4-配列は同じ型のデータを連続したメモリ領域に格納する)
[5. 構造体は異なる型のデータのメモリ領域をまとめて確保する](#5-構造体は異なる型のデータのメモリ領域をまとめて確保する)
[6. スライスはどうやって可変長機能を実現してるのか](#6-スライスはどうやって可変長機能を実現してるのか)
[7. 文字列はバイト配列のポインタを持った構造体](#7-文字列はバイト配列のポインタを持った構造体)
[8. 変数を代入したり、関数の引数に渡すとき、メモリ空間では何が起こっているか](#8-変数を代入したり関数の引数に渡すときメモリ空間では何が起こっているか)
[おわりに](#おわりに)


# 1. アドレスのイメージをつかむ

コンピュータは何かの処理を行うときにメモリを作業場所として使います。メモリは実行中のプログラムや、そのプログラムで扱うデータなど、いろいろな情報を入れる場所です。値を入れるための小さな箱が集まったもの、という認識で構いません。その箱にはメモリ上の場所を表すための「番地」が付けられています。この番地をアドレスと呼びます。

![address.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/f016111f-f076-4b3f-4e5c-1d2ab4402c80.jpeg)


# 2. 変数宣言はアドレスの確保である

変数を宣言すると、その変数用のスペースがメモリ内に確保されます。実際に、次のように`int`型の変数`x`を宣言すると、コンピュータ内部では、

1. `int`型のデータを入れるために$n$番地から$n+k$番地までメモリ領域を確保する
1. $n$番地から$n+k$番地までに`x`という名前を付ける

という処理が行われます。

```go
var x int
```

![int_memory_base (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/835ba058-856e-efc0-6924-54313aa05ae9.jpeg)

変数を宣言する際、具体的なメモリの番地を私たちが指定することはありませんし、その必要もありません。コンピュータが自動的に適切なメモリ領域を選択し、変数に割り当てます。変数は、プログラマーがメモリのアドレスを意識することなくデータを扱えるようにするための仕組みです。

変数に割り当てられたメモリアドレスを知りたい場合、Go言語では、変数の前に`&`記号を付けることでその"**先頭**"のアドレスを取得できます。この操作により、変数のメモリ上の位置を確認することが可能です。

```go: アドレスの確認
package main

import "fmt"

func main() {
	var x int = 10

	fmt.Printf("x  → %d \n", x)
	fmt.Printf("&x → %p \n", &x) // %pは、アドレス値を16進数表記で出力するために使用されます
}
```
```:アドレスの確認: 出力
x  → 10 
&x → 0x400000e080
```

つまり、変数`x`が宣言されると、アドレス<font color="orange">`0x400000e080`</font>を先頭とするいくつかのアドレスが確保され、そのメモリ領域に`x`という名前が割り当てられます。また、変数宣言と同時に値の代入も行われているため、そのメモリ領域に`10`という値が格納されます。そして、以降、変数`x`を使って、メモリスペースに格納されている値`10`を取りにいけるという仕組みです。

![int_memory_concreate.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/1a7922ae-8ec8-189a-eacb-a23585bf10c3.jpeg)

ところで、アドレスは先頭ものしかわからないので、末尾がどこなのかわからないのでは？という疑問が生じます。心配ご無用です。確保するメモリ領域のサイズは変数`x`のデータ型（この場合は`int`）によって決定されます。例えば、64ビットシステムの場合、`int`型は8バイトのメモリ領域を確保します。1バイトごとに1つのアドレスが割り振られているので、先頭から8つ分のアドレスが割り当てられるということになります。

つまり、データ型によってメモリ領域のサイズは自動的に決定されるので、先頭アドレスを知っていれば、変数が占めるメモリ領域の全範囲を把握できることになります。

# 3. ポインタはアドレスを管理する変数

ポインタは**ポインタ変数**と呼ぶのが正確です。つまり、「**ポインタは変数**」なのです。そしてどんな変数かというと、「**アドレスを管理するための変数**」となります。変数なので、それを使う前には必ず変数の宣言を行う必要があります。Go言語では、次のように宣言をします。

```go: ポインタ変数の宣言
var p *int
```

`*int`は「`int`型の値を格納するメモリ領域の先頭アドレスを格納する型」を意味します。なぜ先頭アドレスなのかというと先述した通り、先頭アドレスと型情報があればメモリ領域を一意に特定できるからです。

![point_memory_base.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/790d7c10-33ad-d585-f407-b0a5dc7fddb0.jpeg)

実際に、ポインタ変数にアドレスを代入してみます。

```go: ポインタ変数の宣言とアドレスの代入
package main

import "fmt"

func main() {
	var x int = 10
	fmt.Printf("x  → %d \n" , x)
	fmt.Printf("&x → %p \n" , &x)

	var p *int = &x
	fmt.Printf("p  → %p \n" , p)
	fmt.Printf("&p → %p \n" , &p)
	fmt.Printf("*p → %d \n" , *p)
}
```

```:ポインタ変数の宣言とアドレスの代入: 出力
x  → 10 
&x → 0x40000a4010 
p  → 0x40000a4010 
&p → 0x40000a6020 
*p → 10 
```

ポインタ変数`p`自身の先頭アドレスが<font color="lightblue">`0x40000a6020`</font>であり、そのメモリ領域に変数`x`の先頭アドレス<font color="orange">`0x40000a4010`</font>が格納されています。

ポインタ変数の前に`*`を付けることで、そのポインタが指すメモリ領域(←先頭アドレスと型情報で一意に定められる)に格納されている実際の値にアクセスすることができます。この例では、`*p`を使って`p`が指す`x`の値10にアクセスしています。

![point_memory_concreate.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/b3eb0562-ff15-928a-a62b-a4fcfb4b661b.jpeg)


# 4. 配列は同じ型のデータを連続したメモリ領域に格納する

配列は、「**同じ型のデータを連続したメモリ領域に格納するデータ構造**」です。Go言語のコード例を見てみましょう。

```go: 配列のメモリ領域
package main

import "fmt"

func main() {
	arr := [2]int{}

	fmt.Printf("&arr    → %p \n", &arr)
	fmt.Printf("&arr[0] → %p \n", &arr[0])
	fmt.Printf("&arr[1] → %p \n", &arr[1])
}
```

```sh: 配列のメモリ領域: 出力
&arr    → 0x400000e080 
&arr[0] → 0x400000e080 
&arr[1] → 0x400000e088
```

出力結果をわかりやすく図にすると、以下のようになります。図からわかるように、この配列は`int`型のメモリ領域を連続で２つ確保します。そして、それぞれのメモリ領域を`arr[0]`と`arr[1]`のメモリ領域として利用します。

![arr_memory.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/9acbf23b-5fc0-2884-e4a0-d727011c2ca3.jpeg)

# 5. 構造体は異なる型のデータのメモリ領域をまとめて確保する

次に、構造体のメモリ配置を見てみましょう。構造体は「**異なる型のデータを一つの単位で扱うためのデータ構造**」です。

```go: 構造体のメモリ領域
package main

import "fmt"

func main() {
	var st struct {
		field1 string
		field2 int
	}

	fmt.Printf("&st        → %p \n", &st)
	fmt.Printf("&st.field1 → %p \n", &st.key1)
	fmt.Printf("&st.field2 → %p \n", &st.key2)
}
```

```sh: 構造体のメモリ領域: 出力
&st        → 0x400000c018 
&st.field1 → 0x400000c018 
&st.field2 → 0x400000c028
```

図からわかるように、`st`は`st.field1`と`st.field2`の両方の値を確保するためのメモリ領域を確保します。Go言語の`string`型は（64ビットシステムの場合）16バイトのメモリ領域が割り当てられるので、下図のように`string`型の`st.field1`と`int`型の`st.field2`で異なる大きさのメモリ領域が割り当てられます。

![struct_memory.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/0b476b01-fa3b-79d3-8271-8b1e81045221.jpeg)

# 6. スライスはどうやって可変長機能を実現してるのか

配列は長さが不変でしたが、スライスの長さは可変です。どうやって可変長の機能を実現しているのでしょうか。スライスのメモリ上の姿を確認することで、可変長を実現している仕組みを解き明かしていきましょう。

まずは、配列のときと同じようにスライス自身とスライスの要素の先頭アドレスを確認してみます。

`make`関数を使用してスライスを定義し、先頭アドレスを確認します。
（※ `make`関数の第2, 3引数については後述）

```go: スライスの先頭アドレス
package main

import "fmt"

func main() {
	slice := make([]int, 2, 3)
 	fmt.Printf("slice     → %d \n", slice)
	fmt.Printf("&slice    → %p \n", &slice)
	fmt.Printf("&slice[0] → %p \n", &slice[0])
	fmt.Printf("&slice[1] → %p \n", &slice[1])
}
```

```: スライスの先頭アドレス: 出力
slice     → [0 0]
&slice    → 0x400000c018 
&slice[0] → 0x4000014048 
&slice[1] → 0x4000014050 
```

配列と同様に、`slice[0]`と`slice[1]`のメモリ領域は連続していますが、`slice`と`slice[0]`の先頭アドレスが一致しません。（`0x400000c018` $\not =$ `0x4000014048`）
実は、Go言語のスライスの実体は次のような構造体です。

https://github.com/golang/go/blob/0784fd1b2f6050b8fb91c88c1d02107258a1651a/src/internal/unsafeheader/unsafeheader.go#L22

```go
type Slice struct {
	Data unsafe.Pointer
	Len  int
	Cap  int
}
```

それぞれのフィールドが保持している情報は次のようなものです。

<INS>**`Data`フィールド**</INS>
👉 配列と異なり、スライス自体はデータを直接保持しません。この`Data`フィールドでは、実際にデータを格納しているメモリ領域の先頭アドレスを保持しています。この格納している先頭アドレスを変更することで、動的なサイズ変更を実現しています。詳しくは後述します。

<INS>**`Len`フィールド**</INS>
👉 `Len`フィールドは、スライスが現在保持している要素の数を表します。

<INS>**`Cap`フィールド**</INS>
👉 `Cap`フィールドは、何個分のデータのメモリ領域を確保しているか、を表します。


ここで、`make`関数によるスライス作成のコードを見返してみます。

```go
slice := make([]int, 2, 3)
```

第1引数の`[]int`は`int`型のデータを保持するという宣言です。
第2引数の`2`はデータの初期要素数を指定します。`int`型の場合は値`0`で初期化されます。
第3引数の`3`は確保しておくメモリ領域のサイズを指定しています。`int`型のデータが"3"つ入るスペースを確保するので、8×3=24バイト分の領域を確保することになります。

実際にLenとCapの値を確認してみます。それぞれ`len`関数、`cap`関数を利用して値を取得できます。

```go: スライスのLenとCap
package main

import "fmt"

func main() {
	slice := make([]int, 2, 3)
	fmt.Printf("slice      → %d \n", slice)
	fmt.Printf("len(slice) → %d \n", len(slice))
	fmt.Printf("cap(slice) → %d \n", cap(slice))
	fmt.Printf("&slice     → %p \n", &(slice))
	fmt.Printf("&slice[0]  → %p \n", &slice[0])
	fmt.Printf("&slice[1]  → %p \n", &slice[1])
}
```

```: スライスのLenとCap: 出力
slice      → [0 0] 
len(slice) → 2 
cap(slice) → 3 
&slice     → 0x40000ae000 
&slice[0]  → 0x40000b0000 
&slice[1]  → 0x40000b0008
```

つまり、このスライスはメモリ上でこんな感じになっています。

![slice.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/9b760077-21db-51d6-99ef-4896902f862c.jpeg)

ここで、このスライスに要素を追加してみましょう。

```go: スライスに要素を追加
package main

import "fmt"

func main() {
	slice := make([]int, 2, 3)
	fmt.Printf("slice      → %d \n", slice)
	fmt.Printf("len(slice) → %d \n", len(slice))
	fmt.Printf("cap(slice) → %d \n", cap(slice))
	fmt.Printf("&slice     → %p \n", &(slice))
	fmt.Printf("&slice[0]  → %p \n", &slice[0])
	fmt.Printf("&slice[1]  → %p \n", &slice[1])

	fmt.Printf("--- sliceに要素を追加 --- \n")

	slice = append(slice, 10)
	fmt.Printf("slice      → %d \n", slice)
	fmt.Printf("len(slice) → %d \n", len(slice))
	fmt.Printf("cap(slice) → %d \n", cap(slice))
	fmt.Printf("&slice     → %p \n", &(slice))
	fmt.Printf("&slice[0]  → %p \n", &slice[0])
	fmt.Printf("&slice[1]  → %p \n", &slice[1])
	fmt.Printf("&slice[2]  → %p \n", &slice[2])
}
```
```: スライスに要素を追加: 出力
slice      → [0 0] 
len(slice) → 2 
cap(slice) → 3 
&slice     → 0x400000c018 
&slice[0]  → 0x4000014048 
&slice[1]  → 0x4000014050 
--- sliceに要素を追加 ---
slice      → [0 0 10] 
len(slice) → 3 
cap(slice) → 3 
&slice     → 0x400000c018 
&slice[0]  → 0x4000014048 
&slice[1]  → 0x4000014050 
&slice[2]  → 0x4000014058
```

つまり、要素を追加したことで、メモリ上では次のような変化が起きたことになります。
1. まず、未使用だったメモリ領域に`append`関数で追加した`10`が格納される
1. これにより要素数が3に増えるため、slice構造体の`Len`の値が`2`から`3`に置き換わる

![slice_append (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/01207381-d834-593c-ea3a-a32f6b88a1be.jpeg)


さて、上記のケースの要素の追加では、未使用のメモリ領域が残っていたので、そこに要素が追加されました。では、容量に空きがないスライスに対して、要素を追加するとどうなるでしょうか？試してみましょう。初期要素数と容量をとも`2`にすることで、空き容量がないスライスを作成し、そこに要素を追加してみます。

```go: 空き容量のないスライスへの要素追加
package main

import "fmt"

func main() {
	slice := make([]int, 2, 2)
	fmt.Printf("slice      → %d \n", slice)
	fmt.Printf("len(slice) → %d \n", len(slice))
	fmt.Printf("cap(slice) → %d \n", cap(slice))
	fmt.Printf("&slice     → %p \n", &(slice))
	fmt.Printf("&slice[0]  → %p \n", &slice[0])
	fmt.Printf("&slice[1]  → %p \n", &slice[1])

	fmt.Printf("--- sliceに要素を追加 ---\n")

	slice = append(slice, 10)
	fmt.Printf("slice      → %d \n", slice)
	fmt.Printf("len(slice) → %d \n", len(slice))
	fmt.Printf("cap(slice) → %d \n", cap(slice))
	fmt.Printf("&slice     → %p \n", &(slice))
	fmt.Printf("&slice[0]  → %p \n", &slice[0])
	fmt.Printf("&slice[1]  → %p \n", &slice[1])
	fmt.Printf("&slice[2]  → %p \n", &slice[2])
}
```

```:空き容量のないスライスへの要素追加: 出力
slice      → [0 0] 
len(slice) → 2 
cap(slice) → 2 
&slice     → 0x40000ae000 
&slice[0]  → 0x40000a4010 
&slice[1]  → 0x40000a4018 
--- sliceに要素を追加 ---
slice      → [0 0 10] 
len(slice) → 3 
cap(slice) → 4 
&slice     → 0x40000ae000 
&slice[0]  → 0x4000100000 
&slice[1]  → 0x4000100008 
&slice[2]  → 0x4000100010 
```

つまり、次のようなことが起こっています。

1. まず、スライスには空き容量がないため、現在の容量の2倍の容量をもったメモリ領域を別の場所に新たに確保される
1. その新たな領域に既存要素と追加要素を併せて格納される
1. そして、スライス構造体の`Data`フィールドに格納しているアドレスの値は、旧メモリ領域の先頭アドレスから新メモリ領域の先頭アドレスに更新される

![slice_append_no_cap.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/00a83157-86f0-5900-4a90-8ae5bcdd1825.jpeg)

このようにして、スライスは可変長の機能が実現されているわけです。

# 7. 文字列はバイト配列のポインタを持った構造体

`string`型もスライス同様、実体は次のような構造体です。

https://github.com/golang/go/blob/0784fd1b2f6050b8fb91c88c1d02107258a1651a/src/internal/unsafeheader/unsafeheader.go#L34

```go: string型の実体
type String struct {
	Data unsafe.Pointer
	Len  int
}
```

Go言語では、文字はUTF-8に基づくバイト列の形式で保持されており、文字列構造体はそのバイト配列へのポインター(`Data`フィールド)とバイト配列の長さ(`Len`フィールド)を保持しています。

次のようにして、バイト列の要素や長さを取得できます。



```go: 文字列のバイト配列の長さと要素
package main

import "fmt"

func main() {
    str := "a¥阿😄"
	fmt.Printf("len(str) → %d \n", len(str))

	fmt.Printf("a \n")
	fmt.Printf("str[0]   → %x \n", str[0])

	fmt.Printf("¥ \n")
	fmt.Printf("str[1]   → %x \n", str[1])
	fmt.Printf("str[2]   → %x \n", str[2])

	fmt.Printf("阿 \n")
	fmt.Printf("str[3]   → %x \n", str[3])
	fmt.Printf("str[4]   → %x \n", str[4])
	fmt.Printf("str[5]   → %x \n", str[5])

	fmt.Printf("😄 \n")
	fmt.Printf("str[6]   → %x \n", str[6])
	fmt.Printf("str[7]   → %x \n", str[7])
	fmt.Printf("str[8]   → %x \n", str[8])
	fmt.Printf("str[9]   → %x \n", str[9])
}
```
```:文字列のバイト配列の長さと要素: 出力
len(str) → 10 
a 
str[0]   → 61 
¥ 
str[1]   → c2 
str[2]   → a5 
阿 
str[3]   → e9 
str[4]   → 98 
str[5]   → bf 
😄 
str[6]   → f0 
str[7]   → 9f 
str[8]   → 98 
str[9]   → 84
```

他の言語を書いてきた身からすると、`str[0]`としたら文字列の最初の文字`a`が取得されたり、`len(str)`としたら文字数`4`が取得されると思ってしまいますが、あくまで文字をUTF-8形式のバイト列で表現した場合の要素や長さが取得されます。

UTF-8において、
* `a`は、1バイト(0x61)で表現されるので、0番目の要素`str[0]`
* `¥`は、2バイト(0xc2a5)で表現されるので、1, 2番目の要素`str[1]`, `str[2]
* `阿`は、3バイト(0xe998bf)で表現されるので、3, 4, 5番目の要素`str[3]`, `str[4]`, `str[5]
* `😄`は、4バイト(0xf09f9884)で表現されるので、6, 7, 8, 9番目の要素`str[6]`, `str[7]`, `str[8]`, `str[9]`

がそれぞれの文字に対応するバイト列になります。そして、合計10バイトになるので、`len(str)`は`10`になっています。このようにUTF-8は文字を1〜4バイトで表現されます。

### 💡 `+=`による文字列結合のコストが高い理由

ところで、`string`型はスライスと異なり、`Cap`フィールドがありません。ここからもわかるように、`string`型は初期化時に余分に容量を確保していません。そのため、次のように`+=`で文字列結合を行うと、新たにメモリ領域を確保しなおして、元の文字列をそこにコピーし、結合する文字列を追加するという処理が実行されます。これでは、使用しないメモリ領域が一時的に残ってしまったり、文字列のコピーのコストがかかったりと、効率のよい処理とは言えません。

```go: +=による文字列結合
text := "Go言語"
text += "楽しい"
```

このコストを下げるには、内部的に余分な容量をいい感じで確保してくれて、新しい領域を確保することなく、元の領域に連続する形で新たな文字列を追加してくれる機能があればよいわけです。

そして、もちろんそのような機能は提供されています。詳しくは下記の記事などを参考にしていただければと思います。

https://text.baldanders.info/golang/join-strings-2/


# 8. 変数を代入したり、関数の引数に渡すとき、メモリ空間では何が起こっているか

変数を別の変数に代入したり、関数の引数に渡すことで、変数が保持している値の情報を共有することができますが、このときメモリ空間上では何が起こっているでしょうか？

変数(または引数)`y`に変数`x`の値が共有されるプロセスはこうです。

1. 変数`x`と同じ容量のメモリ領域が確保される
1. 新たなメモリ領域に`x`のメモリ領域に格納されている値がコピーされる
1. 新たなメモリ領域と変数(または引数)`y`が紐づけられる

```go: 別の変数への代入
package main

import "fmt"

func main() {
	x := 10
	fmt.Printf("x  → %d \n", x)
	fmt.Printf("&x → %p \n", &x)

	y := x
	fmt.Printf("y  → %d \n", y)
	fmt.Printf("&y → %p \n", &y)
}
```
```:別の変数への代入: 出力
x  → 10 
&x → 0x400000e080 
y  → 10 
&y → 0x400000e0a0
```

![assign.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/223d3179-005c-961f-3aae-cda5c703de7c.jpeg)

### 💡 関数やメソッド内で構造体のデータの値を変えたいときは、ポインタを渡す

構造体を関数の引数やメソッドのレシーバーに渡しても、もれなく値がコピーされるので、関数やメソッド内で値を変更しても元の構造体の値は変化しません。渡した構造体のデータを変更したい場合は、その構造体のポインタを渡せばいいわけです。そうすれば、渡した構造体のメモリ領域に間接的にアクセスできます。

```go:構造体自身を渡すかポインタを渡すか
package main

import "fmt"

type Struct struct {
	field1 int;
	field2 int;
}

func func1(y Struct) {
	y.field1 = 30
}

func func2(p *Struct) {
	p.field2 = 40
}

func main() {
	x := Struct{10, 20}

	func1(x)
	func2(&x)

	fmt.Printf("x.field1 → %d \n", x.field1)
	fmt.Printf("x.field2 → %d \n", x.field2)
}
```
```:構造体自身を渡すかポインタを渡すか: 出力
x.field1 → 10 
x.field2 → 40
```

![struct_args (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/67640a8a-b812-7b38-6d3c-5326ff57855d.jpeg)

ところで、`func2`内のコードですが、`p`はポインタ変数だから、実体である`x`にアクセスするには、次のように書くのが正しいのではないかと思われます。

```go
func func2(p *Struct) {
	// p.field2 = 40　ではなく
    (*p).field2 = 40
}
```

もちろん、これは正しい記法です。実は構造体のポインタはその実体へアクセスする際に`*`を省略できるのです。毎回`*`を書かずに済むので便利です。

### 💡 ポインタを持った構造体には要注意

ポインタを持った構造体、例えばスライスなどを代入したり、引数として渡す場合は少し注意が必要です。
次のような例を見てみましょう。

```go: スライスの代入
package main

import "fmt"

func main() {
	slice1 := []int{10, 20}
	slice2 := slice1
	slice2[0] = 100

	fmt.Printf("slice1[0] → %d \n", slice1[0])
	fmt.Printf("slice2[0] → %d \n", slice2[0])
}
```

```:スライスの代入: 出力
slice1[0] → 100 
slice2[0] → 100
```

`slice2[0]`の値を`100`に変更したのに、`slice1[0]`の値も`100`になってしまいました。これは、スライスがデータ自体を持たず、データ配列へのポインタを保持していることに由来します。スライスの代入時にコピーされるのは、`Data`・`Len`・`Cap`のフィールドを持ったスライス構造体だけで、実際のデータ配列はコピーされないのです。

```go: スライス構造体: 再掲
type Slice struct {
	Data unsafe.Pointer
	Len  int
	Cap  int
}
```

要するにメモリ空間はこんな感じになっています。

![slice_assign (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1166480/917a5950-c2fd-4aa7-4da4-49814810c560.jpeg)

スライス構造体自体の値はコピーされていますが、その向き先のデータ配列は同じなので、`slice2[0]`の値を変更するとすると同じデータ配列を参照している`slice1[0]`の値も変更されてしまったわけです。

# おわりに

私がGo言語を学んで、つまづいたポイントはまだまだありますが、この記事に書かれた基本的な内容を理解して、メモリの世界に少しでも親しんだことで、多くの問題が理解しやすくなりました。もし、あなたも私と同じように、これまでメモリについて深く考えたことがなかったのなら、この記事が少しでも役立てば嬉しいです。