はじめに
本記事は、Go言語のポインタについて について記述しております!!
本記事で学べること
本記事を通して学べることは、
- ポインタとは
- Go言語でのポインタの使用方法
本記事の流れ
目次 |
---|
1. ポインタとは |
2. ポインタを理解する上で必要なこと |
3. Go言語でのポインタの使用方法 |
1. ポインタとは
ポインタとは、変数などのメモリのアドレスを保持する特殊な変数です。
このアドレスは、メモリ上の位置を示しポインタを通じてその位置にアクセスしてデータの読み書きすることができる。
ポインタを知るには、コンパイル・変数・メモリ・アドレス
を理解する必要があります。
それでは、ポインタを理解できるように前提の話をしていきます。
2. ポインタを理解する上で必要なこと
コンパイル
Go言語は、記述したソースコードをそのままコンピュータに読ますことが出来ないため一度コンパイルをしてバイナリーコード(コンピュータが読めるコード)に変更する必要があります。
→バイナリーコードとは、コンピュータが読みやすい言葉で0 or 1
この2つの数字で書かれているものです。
このコンパイルの過程で変数はメモリ上の特定の位置に割り当てられます。
その位置をアドレスと言います。
そのコンパイル時に
要するに、、、
コンパイル時にメモリ上のどこかに変数を割り当ててその割り当てた場所
のことをアドレスという
そして、ポインタを理解する上で必要になってくるのが、変数
メモリ
アドレス
です。
これらを理解することによりポインタへの理解が深まります。
コードで説明をしていきます。
package main
import "fmt"
func main() {
variable := "変数"
}
go run main.go
go run main.go
のコマンドを実行することにより指定ファイルをコンパイル→実行をしてくれます。
コンパイルの際にmain.go
ファイルのmain関数
に記述されている、variable
という変数がメモリのどこかに割り当てられます。
そしてその変数が割り当てられた場所がアドレスになります。
次は、そのアドレスを実際に表示させましょう。
アドレスの出力
Go言語で実際にアドレスを表示していきたいと思います。
Go言語でアドレス
を表示させるためには、&
を使用します。
package main
import "fmt"
func main() {
variable := "変数"
fmt.Println(&variable) // variableのメモリアドレスを表示
}
0xc00009e020
このような値が出力されればアドレス
の出力の成功です。
上記のように&
を使用して変数のアドレスを出力させることができます。
アドレスを使用できるようになると、どのようなメリットがあるというとメモリのアドレスに格納された変数の値が変更できる
というメリットがあります。
例えば
package main
import "fmt"
type Product struct {
Name string
Price int
}
func main() {
var p Product
p = Product{
Name: "コーヒー",
Price: 200,
}
p2 := &p
p2.Name = "紅茶"
p2.Price = 300
fmt.Print(p)
fmt.Print(p2)
}
{紅茶 300}&{紅茶 300}
上記のコードのように書くとp
/ p2
が同じ値になります。
なぜ値が同じになっているかを説明するためにコードを順に解説すると
- Productという構造体の
変数p
を定義する - pという変数の中に値を入れる
Name: "コーヒー"
/Price: 200
-
&p
でアドレスを指しているのでp2
には、pのアドレス
を変数の値として格納します。 - これによりp2という変数名ですが、pとp2は同じものを指しているようになります。
- その上でp2(pのアドレス)のNameとPriceを変更させているので
p
のアドレスにあるName / Priceの値を変更しているようになる
上記の流れによりp2を変更するとpの値を変更されることになります。
ポインタ があることにより値だけを渡す方法
と変数のアドレスを渡し元の変数の値自体を書き換える
ことができます。
それをコードに書いていきます。
package main
import "fmt"
type Product struct {
Name string
Price int
}
func main() {
var p Product
p = Product{
Name: "コーヒー",
Price: 200,
}
// pの値を表示
fmt.Print(p)
// pの値をだけを渡す方法(p2を変更してもpの値は変更されない)
p2 := p
p2.Name = "カフェラテ"
p2.Price = 400
// pのアドレスを変数に格納をする(p3はpのアドレスなのでp3を変更すると`p`の値が書き換わる)
p3 := &p
p3.Name = "紅茶"
p3.Price = 300
// p3によって書き換えられた値
fmt.Print(p)
// p2の値
fmt.Print(p2)
// p3の値
fmt.Print(p3)
}
{コーヒー 200}
{紅茶 300}
{カフェラテ 400}
&{紅茶 300}
上記のように出力されます
コードの説明をしていきます。
type Product struct {
Name string
Price int
}
Product
という構造体の定義
var p Product
p = Product{
Name: "コーヒー",
Price: 200,
}
// pの値を表示
fmt.Print(p)
Productという構造体のp
変数を宣言する
p
の変数に値を格納している
p
の値を出力している
// pの値をだけを渡す方法(p2を変更してもpの値は変更されない)
p2 := p
p2.Name = "カフェラテ"
p2.Price = 400
p
の変数の値をp2
に格納している
- この時に
p
は、p2
に対して 値のみ を渡している(値渡し)
p2の値に対してNameには、カフェラテ
/ Priceには、400
を入れている。
// pのアドレスを変数に格納をする(p3はpのアドレスなのでp3を変更すると`p`の値が書き換わる)
p3 := &p
p3.Name = "紅茶"
p3.Price = 300
p
の変数のアドレスをp3
に格納している
- この時に
p3
に格納しているのは、p
のアドレスをp3
に格納しているつまり、p3
はp
と同じアドレスを指していることになります。(参照渡し)
p3の値に対してNameには、紅茶
/ Priceには、300
を入れている。
※ ここでp2
と大きく違うのは、p2
はあくまでp
から値を渡されただけでp2とpは 違うアドレス なのでp2の値を変更したとしてもpに対して何も影響が出ないのに対してp3
はp
と同じアドレスを指しているためp3が値の変更があった場合p
もその変化に伴ってp
の値も変化するということになる。
このようにしてポインタを使用した場合と使用していない場合でコードの使い分けができます。
これでポインタについての理解が少し深まったと思います。
それでは次にポインタ型について説明をしていきます。
ポインタ型
ポインタ型とは、メモリ上のアドレスを記憶する変数の型
※ポインタ型は、派生型なので単体での型定義はできない(Int Stringなどと併用して使用する)
下記に例を記述します。
package main
import "fmt"
type Product struct {
Name string
Price int
}
func main() {
var p *Product
p = &Product{
Name: "太郎",
Price: 200,
}
fmt.Print(p)
}
&{太郎 200}
コードの説明をしていきます。
※これまでに説明をしてきた構造体の定義などは、省略させていただきます。
var p *Product
p = &Product{
Name: "太郎",
Price: 200,
}
fmt.Print(p)
*Product
の*
によってProductのポインタ型として宣言している。
そしてpがポインタ変数
として宣言をしており値の格納をしている
上記の内容は、Productという構造体を*
を使用してポインタ型として宣言をしておりポインタ型を格納しているその構造体を変数p
として宣言している。
※ポインタ型の構造体の変数のことをポインタ変数
と呼ぶ
応用
次にこれまでの内容から応用を一つ出していきたいと思います。
- 変数を宣言する
- 宣言した変数を新たに宣言した変数に
参照渡し
で値を格納する - 参照渡しをした変数に対して値の変更をする
方法について記述をしていきます。
package main
import "fmt"
func main() {
product := "コーヒー"
// productのアドレスをp2に格納する→(p2に入るのは、productのアドレスとそのアドレスにあるproduct="コーヒー"→*string)
p2 := &product
// p2の値を変更するのに必要なのはアスタリスク(*)が必要
*p2 = "紅茶"
// p2のアドレス(productのアドレス)
fmt.Print(p2)
fmt.Printf(product)
// p2の値(productの値)
fmt.Printf(*p2)
}
このようにしてp2側でpの値の変更が可能になる
最後に
今回は、Go言語のポインタについて記述いたしました。
ご指摘点などがございましたら、コメントをいただけると幸いです。