1
1

Goのポインタ基礎知識まとめ

Posted at

これは、「Happiness Chain Advent Calendar 2023」の 17 日目の記事です。

はじめに

現在、Happiness Chain で Go の学習をしています。
その際、Go の「ポインタ」が自分としてはあまり馴染みがなく、使っていてもいまいちしっくりとこなかったり、使いどころがわからなかったりしているので、知識の整理をしていきたいと思います!

ポインタとは

ポインタとは、値型に分類されるデータ構造のメモリ上の「アドレス」と「型」の情報のことです。

たとえば、aという変数宣言をした際に、その変数用にメモリの領域を確保します。
このとき、その確保した場所を示すのが「アドレス」です。
0xc0000120d0」というような値として表示されます。

main.go
package main

import "fmt"

func main() {
	p := 3
	ip := &p
	fmt.Printf("type=%T, address=%p", ip, ip)
}
実行結果
// ipはintのポインタ型で、その値が「0xc0000120d0」となる
type=*int, address=0xc0000120d0

ポインタの定義と値の参照方法

ポインタの定義

ポインタは、「int」や「float」などポインタを使って参照・操作したい型の前に「*」を置くことで定義します。

main.go
var ip *int // int型のポインタ
var fp *float64 // float64型のポインタ

任意の型からポインタ型を作成

アドレス演算子「&」を使うことで、任意の型からポインタ型を作成することができます。

main.go
var i int // int型の変数
ip := &i // int型のポインタを作成

fmt.Printf("%T\n", ip)
実行結果
*int 

ポインタ型の変数から値を参照する方法

ポインタ型が保持するメモリ上のアドレスを使用してデータ本体を参照する仕組みのことをデリファレンスといいます。
デリファレンスをするには、ポインタ型の前に「*」を配置します。

main.go
package main

import "fmt"

func main() {
	var i int // int型の変数
	ip := &i  // int型のポインタを作成
	i = 3

	fmt.Printf("ip = %v\n", ip)
	fmt.Printf("*ip = %v\n", *ip)
}

ポインタを操作して値を書き換える場合も、デリファレンスを用います。

実行結果
ip = 0xc000112008 // ipの値(アドレス)
*ip = 3 // デリファレンスで変数iの値を参照
main.go
package main

import "fmt"

func main() {
	var i int // int型の変数
	ip := &i  // int型のポインタを作成
	i = 3

	fmt.Printf("i = %v\n", i)
	fmt.Printf("ip = %v\n", ip)
	fmt.Printf("*ip = %v\n", *ip)

	*ip = 100

	fmt.Printf("i = %v\n", i)
	fmt.Printf("ip = %v\n", ip)
	fmt.Printf("*ip = %v\n", *ip)
}
実行結果
i = 3
ip = 0xc0000120d0
*ip = 3

i = 100 // 変数iを示すポインタ変数ipの値を参照し、書き換えたため、iの値も変わる
ip = 0xc0000120d0
*ip = 100

ポインタの使いどころ

上記の通り、ポインタの基本的な概念と、その使い方は整理できました。
次は、ポインタを使うときと使わないところの違いがよくわからないため、「ポインタ」を使う主な場面をまとめたいと思います。

  • 引数やレシーバ(Go で構造体のフィールドやその他のデータ型にアクセスする方法を提供するメソッドの引数)の値を関数内で書き換えたいとき
    • 引数やレシーバにポインタを使わな場合、その関数内で値を書き換えても、引数やレシーバの値は変わることはありません
    • これは、引数やレシーバに当てはめたときにはコピーを作成することになり、コピーを変更することになるためです
    • 書き換えたい場合は、ポインタを使う必要があります
  • コピーを避けたいデータを引数、レシーバにする場合
    • 構造体にはコピーが発生すると問題が生じるものがあり、そのような構造体はポインタで扱うことで、コピーをされないようにします
  • コピーのコストが大きくなるような、大きな構造体や配列を扱う場合や大きな構造体をスライスに持たせる場合
    • 大きな構造体や配列は、コピーのコストが大きいため、ポインタで扱うのが良いです

おわりに

以上、ポインタについてまとめさせていただきました。
この場合はポインタを使った方が良い、というとことがまとめる前よりも理解できたような気がするので、これからさらに学習を進める上で、うまく利用をしてきたいと思います!

参照

1
1
0

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
1
1