最近学習しているGo言語において、基礎であるポインタについてメモがてらまとめてみます!Javaにはポインタがありませんので、比較も交えながら解説してみます!
ポインタ
前提として変数はメモリ上で管理されていますが、そのメモリにおけるアドレスと実態の管理について図解を用いて解説してみます!
用語と書き方
その前に、簡単に用語や書き方に関して。
ポインタ関連の用語は図示したような感じで、 関節演算子* を用いて ポインタ型を宣言したりデリファレンスすることができ、&を使ってアドレスを表現することができます!
- &(アドレス演算子):変数のアドレスを表す
- *(関節演算子):ポインタ型を宣言したり、デリファレンスに活用する
- ポインタ型:*int のように *type 形式 で宣言する型
- ポインタ型変数:p のようにポインタ型で宣言された変数※アドレスのみ格納可能
- アドレス:&n のように&variable で表記する値
- デリファレンス:*p のように*ポインタ変数の形式で実態を参照すること
図解
package main
import "fmt"
func main() {
//変数
var n int = 10
// 値を表示
fmt.Println(n) // 10
// nのアドレスを表示
fmt.Println(&n) // 0x1400012c008
// ポインタ型*intのポインタ変数pにnのアドレス&nを格納
var p *int = &n
// アドレスを表示
fmt.Println(p) // 0x1400012c008
// アドレスの実態を表示
fmt.Println(*p) // 10
// ポインタ型変数pのアドレスを表示
fmt.Println(&p) // 0x1400012c010
}
↑のようなソースが合った際にメモリではこのようにデータ管理されています!
要は何なのか?
ソースや図解をみて実際にデータ管理されているかわかったと思いますが、だから何なの?と思いますよね。
要は値渡しと参照渡しの違いが重要だと思います!Javaを用いて簡単に比較するとこんな感じです!
Goにはクラスがなくその代わりに構造体という概念があります。以下では値渡しと参照渡しの違いについてソースにまとめています。
また、構造体にはレシーバ(Javaでいうセッターなどのクラスメソッド)という概念があり、2種類存在します。値レシーバは値渡しとなり、ポインタレシーバは参照渡しで構造体のプロパティにアクセスします。
package main
import "fmt"
// 構造体
type Vertex struct {
X int
Y int
}
// 値レシーバ
func (v Vertex) setByVariable(x, y int) {
v.X = x
v.Y = y
}
// ポインタレシーバ
func (v *Vertex) setByPointer(x, y int) {
v.X = x
v.Y = y
}
func main() {
// 構造体
v := Vertex{1, 2}
fmt.Println(v) // {1 2}
// 値渡し!
v.setByVariable(3, 4)
fmt.Println(v) // {1 2} :更新されない!
// 参照渡し!
v.setByPointer(5, 6)
fmt.Println(v) // {5 6} :更新されている!
}
値レシーバを介して構造体に値をセットしようとした場合は、値渡しになるため構造体の値に変更がありません。対して、ポインタレシーバを介して構造体の値を更新した場合は実際に値が更新されます!
Javaにおいても、引数にてint型・Integer型を渡した場合では値渡し・参照渡しで動作が異なりますよね。(Javaでは基本データ型以外は全て参照型なので、finalなどを付けてておかないと副作用のあるメソッドになってしまったりで、それはそれで気をつけないといけないですね。)
まとめ
今回はポインタについてまとめてみましたが、今度は newやmakeの違いなどについてもまとめてみようと思います!
学習サイト
こちらのUdemyで学習をしています!どちらも丁寧なのでオススメです!