0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Go言語のポインタ、一緒に仲良くなろう!

Last updated at Posted at 2023-06-25

はじめに

本記事は、Go言語のポインタについて について記述しております!!

本記事で学べること

本記事を通して学べることは、

  • ポインタとは
  • Go言語でのポインタの使用方法

本記事の流れ

目次
1. ポインタとは
2. ポインタを理解する上で必要なこと
3. Go言語でのポインタの使用方法

1. ポインタとは

ポインタとは、変数などのメモリのアドレスを保持する特殊な変数です。
このアドレスは、メモリ上の位置を示しポインタを通じてその位置にアクセスしてデータの読み書きすることができる。
ポインタを知るには、コンパイル・変数・メモリ・アドレスを理解する必要があります。
それでは、ポインタを理解できるように前提の話をしていきます。

2. ポインタを理解する上で必要なこと

コンパイル
Go言語は、記述したソースコードをそのままコンピュータに読ますことが出来ないため一度コンパイルをしてバイナリーコード(コンピュータが読めるコード)に変更する必要があります。
バイナリーコードとは、コンピュータが読みやすい言葉で0 or 1この2つの数字で書かれているものです。

このコンパイルの過程で変数はメモリ上の特定の位置に割り当てられます。
その位置をアドレスと言います。
そのコンパイル時に
要するに、、、
コンパイル時にメモリ上のどこかに変数を割り当ててその割り当てた場所のことをアドレスという

そして、ポインタを理解する上で必要になってくるのが、変数 メモリ アドレス です。
これらを理解することによりポインタへの理解が深まります。

コードで説明をしていきます。

main.go
package main

import "fmt"

func main() {
    variable := "変数"
}
go run main.go

go run main.go のコマンドを実行することにより指定ファイルをコンパイル→実行をしてくれます。
コンパイルの際にmain.goファイルのmain関数に記述されている、variableという変数がメモリのどこかに割り当てられます。
そしてその変数が割り当てられた場所がアドレスになります。

次は、そのアドレスを実際に表示させましょう。

アドレスの出力

Go言語で実際にアドレスを表示していきたいと思います。
Go言語でアドレスを表示させるためには、&を使用します。

main.go
package main

import "fmt"

func main() {
    variable := "変数"
    fmt.Println(&variable) // variableのメモリアドレスを表示
}
0xc00009e020

このような値が出力されればアドレスの出力の成功です。
上記のように&を使用して変数のアドレスを出力させることができます。

アドレスを使用できるようになると、どのようなメリットがあるというとメモリのアドレスに格納された変数の値が変更できるというメリットがあります。

例えば

main.go
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の値を変更されることになります。

ポインタ があることにより値だけを渡す方法変数のアドレスを渡し元の変数の値自体を書き換えることができます。

それをコードに書いていきます。

main.go
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}

上記のように出力されます
コードの説明をしていきます。

main.go
type Product struct {
	Name  string
	Price int
}

Productという構造体の定義

main.go
	var p Product

	p = Product{
		Name:  "コーヒー",
		Price: 200,
	}
    // pの値を表示
	fmt.Print(p)

Productという構造体のp変数を宣言する
pの変数に値を格納している
pの値を出力している

main.go
    // pの値をだけを渡す方法(p2を変更してもpの値は変更されない)
    p2 := p
	p2.Name = "カフェラテ"
	p2.Price = 400

pの変数の値をp2に格納している

  • この時にpは、p2に対して 値のみ を渡している(値渡し)

p2の値に対してNameには、カフェラテ / Priceには、400 を入れている。

main.go
    // pのアドレスを変数に格納をする(p3はpのアドレスなのでp3を変更すると`p`の値が書き換わる)
	p3 := &p
	p3.Name = "紅茶"
	p3.Price = 300

pの変数のアドレスをp3に格納している

  • この時にp3に格納しているのは、pのアドレスをp3に格納しているつまり、p3pと同じアドレスを指していることになります。(参照渡し)

p3の値に対してNameには、紅茶 / Priceには、300 を入れている。
※ ここでp2と大きく違うのは、p2はあくまでpから値を渡されただけでp2とpは 違うアドレス なのでp2の値を変更したとしてもpに対して何も影響が出ないのに対してp3pと同じアドレスを指しているためp3が値の変更があった場合pもその変化に伴ってpの値も変化するということになる。

このようにしてポインタを使用した場合と使用していない場合でコードの使い分けができます。

これでポインタについての理解が少し深まったと思います。
それでは次にポインタ型について説明をしていきます。

ポインタ型

ポインタ型とは、メモリ上のアドレスを記憶する変数の型
※ポインタ型は、派生型なので単体での型定義はできない(Int Stringなどと併用して使用する)

下記に例を記述します。

main.go
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}

コードの説明をしていきます。
※これまでに説明をしてきた構造体の定義などは、省略させていただきます。

main.go
    var p *Product

    p = &Product{
        Name: "太郎",
        Price:  200,
    }
    fmt.Print(p)

*Product*によってProductのポインタ型として宣言している。
そしてpがポインタ変数として宣言をしており値の格納をしている

上記の内容は、Productという構造体を*を使用してポインタ型として宣言をしておりポインタ型を格納しているその構造体を変数pとして宣言している。
※ポインタ型の構造体の変数のことをポインタ変数と呼ぶ

応用

次にこれまでの内容から応用を一つ出していきたいと思います。

  • 変数を宣言する
  • 宣言した変数を新たに宣言した変数に参照渡しで値を格納する
  • 参照渡しをした変数に対して値の変更をする

方法について記述をしていきます。

main.go
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言語のポインタについて記述いたしました。

ご指摘点などがございましたら、コメントをいただけると幸いです。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?