LoginSignup
59
29

【Goのやさしい記事】Goのポインタを5分で学ぼう

Last updated at Posted at 2019-11-30

はじめに

今日から3日連続で、Go初心者向けのやさしい記事を公開します。
初日はポインタです。

プログミング初心者がつまずきやすい、Goのポインタまわりをなるべく簡潔に分かりやすくまとめました。
C言語よりは学習ハードルが低いので安心してください。

zozo.go

アドレスとポインタ

アドレス

アドレスとは、16進数で表現されたメモリの番地(場所)のことです。
& (アドレス演算子)を変数の前に付けることで任意の型からポインタ型を定義できます。(Cも同様)
ポインタ型が格納された変数のことをポインタ変数と呼びます。下でいえば、 b がポインタ変数になります。

func main() {
	var a int
	fmt.Println(&a)

	b := &a
	fmt.Println(b)
}
実行結果
0xc000014090
0xc000014090

ポインタ変数から値を参照するためには * を変数の前に付けます。これをデリファレンスと呼びます。

func main() {
	a := 1
	b := &a
	fmt.Println(*b)
}
実行結果
1

ポインタ

ポインタとは、「型情報+アドレス」のことです。

func main() {
	a := 1
	b := &a
	fmt.Printf("型情報: %T /アドレス/: %p", b, b)
}
実行結果
型情報: *int /アドレス: 0xc00001e060

*の前に付けることでポインタ型を定義できます。
初期値はnilです。

func main() {
	var a *int // 変数aはint型のポインタ型
	fmt.Println(a)
}
実行結果
<nil>

また、 * を複数つけて「ポインタのポインタの・・・」みたいことができます。
とはいえ、普段のソフトウェア開発で使用するケースはほとんどないでしょう。

func main() {
	var a *****int
	fmt.Println(a)
}
実行結果
<nil>

ポインタがあると何が嬉しいか?

ここまで、淡々と説明してきましたが、これで何ができるようになるのでしょうか?
それは、値型の変数をポインタの値渡しできることです。

値渡し

package main

import "fmt"

func sampleFunc(a int) {
	a += 1
	fmt.Println("②:", a)
}

func main() {
	a := 10
	fmt.Println("①:", a)
	sampleFunc(a)
	fmt.Println("③:", a)
}
実行結果
①: 10
②: 11
③: 10

注目は③です。渡したのはコピーなのでもとの値は変わりません。
これは、sampleFunc(a) で値渡しをしているからです。

ポインタの値渡し

package main

import "fmt"

func sampleFunc(a *int) {
	*a += 1
	fmt.Println("②:", *a)
}

func main() {
	a := 10
	fmt.Println("①:", a)
	sampleFunc(&a)
	fmt.Println("③:", a)
}
実行結果
①: 10
②: 11
③: 11

今度の③は変わります。
sampleFunc(&a) で渡したのが変数aのアドレスなので、渡した方も渡された方も同じものを見ていることになります。

また、参照型の変数であるスライスやマップの場合は、ポインタ型を渡さなくてももとからポインタの値渡しになっています。

スライスの例
func sampleFunc(a []int) {
	a[0] = 2
	fmt.Println("②:", a)
}

func main() {
	a := make([]int, 1)
	a[0] = 1
	fmt.Println("①:", a)
	sampleFunc(a)
	fmt.Println("③:", a)
}
実行結果
①: [1]
②: [2]
③: [2]
マップの例
package main

import "fmt"

func sampleFunc(a map[int]string) {
	a[0] = "tokyo"
	fmt.Println("②:", a)
}

func main() {
	a := make(map[int]string)
	a[0] = "osaka"
	fmt.Println("①:", a)
	sampleFunc(a)
	fmt.Println("③:", a)
}
実行結果
①: map[0:osaka]
②: map[0:tokyo]
③: map[0:tokyo]

Goでは参照型の変数はスライスとマップとチャネルの3つだけです。(残りは値型かポインタ型です。)

まとめると、ポインタの値渡しをするためには「値型の変数であればポインタを渡す」、「参照型の変数であればそのまま渡す」ということになります。

ちなみにですが、Goには「参照渡し」は存在せず、すべて「値渡し」となります。

参考

スターティングGo言語

少し古いですが、Goの文法を書籍で勉強するならこれ一択な気がします。

さいごに

明日は、【Go初心者向けのやさしい記事】Goのインターフェースを10分で学ぼうという記事です。こちらもお楽しみに!

59
29
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
29