Help us understand the problem. What is going on with this article?

Goのポインタを学ぶ

変数とアドレス

ポインタを理解するための第一歩として「変数」と「アドレス」があります。変数とは、値を保存しておくためのメモリ領域に名前をつけて管理するものであり、アドレスとは、変数がメモリ領域のどこに格納されているかを示すものです。

コンピュータのメモリは、横一列に並べられた巨大なロッカーをイメージするとわかりやすいと思います(って偉い人が)。ロッカーには端から端まで一意の番号が振られています。ロッカーを探すとき、例えば8桁の番号で表されても面倒なのでロッカーには名前を書いておこうというわけです(実際には16進数で表されます)。

package main

import "fmt"

func main() {
    var hoge string
    hoge = "Hello"

    fmt.Println("hogeの値:", hoge)
    fmt.Println("hogeのアドレス:",&hoge)
}
hogeの値: Hello
hogeのアドレス: 0xc0000101e0

実行結果を見てみると、Helloという文字列が0xc0000101e0番地のメモリ領域に変数名hogeとして格納されていることがわかります。注意点として、変数が格納されるアドレスは実行環境によって異なります。再実行してみると変数が別のアドレスに格納されることがわかると思います(変わらないこともあります)。

このように変数とアドレスは切っても切れない関係にあります。

ポインタ

ポインタとは、変数のアドレスを示すものです。変数のアドレスを示す変数を「ポインタ変数」、その型を「ポインタ型」といいます。

package main

import "fmt"

func main() {
    var hoge string
    hoge = "Hello"

    var p *string
    p = &hoge

    fmt.Println("hogeの値:", hoge)
    fmt.Println("hogeのアドレス:",p)
    fmt.Println("pが指すアドレスの値:",*p)
}
hogeの値: Hello
hogeのアドレス: 0xc000092030
pが指すアドレスの値: Hello

ポインタ型を定義するには既存の型に*を付けます。ここではstring型の変数のアドレスを保持したいので、*stringと書きます。また、変数のアドレスを取得したい場合には、変数にアドレス演算子&を用います。ここで取得したいのはhogeのアドレスなので、&hogeと書きます。つまり、ポインタ変数phogeのアドレスを保持していることになります。

逆にポインタ変数からアドレス内の値を知りたい場合には、ポインタ変数に間接参照演算子*を用います。実行結果は、pが指すアドレス0xc000092030にはHelloが格納されていることを示しています。

ちなみに、ポインタ変数のゼロ値はnilです。そりゃそうだ。

    var p *string
    fmt.Println(p)
<nil>

変数とポインタ変数

変数とポインタ変数の使い分けについて考えたいと思います。

package main

import "fmt"

func main() {
    var hoge string
    hoge = "Hello"

    var fuga string
    fuga = hoge

    var piyo *string
    piyo = &hoge

    fmt.Println("hogeの値:", hoge)
    fmt.Println("fugaの値:", fuga)
    fmt.Println("piyoが指すアドレスの値:", *piyo)

    fuga = "World"
    fmt.Println("fuga更新後のhoge:", hoge)

    *piyo = "World"
    fmt.Println("piyoが指すアドレスの値更新後のhoge:", hoge)

    fmt.Println("hogeのアドレス:", &hoge)
    fmt.Println("fugaのアドレス:", &fuga)
    fmt.Println("piyoが指すアドレス", piyo)
}
hogeの値: Hello
fugaの値: Hello
piyoが指すアドレスの値: Hello

fuga更新後のhoge: Hello
piyoが指すアドレスの値更新後のhoge: World

hogeのアドレス: 0xc000088030
fugaのアドレス: 0xc000088040
piyoが指すアドレス 0xc000088030

fugaには単なる変数hogeを代入しています。2つの変数は同じ値を保持していますが、それぞれの変数が格納されたアドレスを確認してみるとその値は異なっています。これはメモリ上の別のアドレスに同じ値を持った2つの変数が存在していることを示しています。当たり前ですが、2つは別の変数ですのでfugaの値を変更したとしても、それがhogeへ反映されることはありません。

piyoにはhogeのアドレスを代入しています。hogeが格納されているアドレスとpiyoが指すアドレスは同じなので、間接参照演算子を用いて*piyoの値を変更すると、それはhogeへも反映されます。

このような機能から、Goにおけるポインタは、構造体のメソッドで何らかの処理を行う場合にポインタレシーバを用いるケースが多く見られます。引数やレシーバで何らかの値を受け取り、その処理を呼び出し元に反映させるにはポインタを用いる必要があるということです。

参考リンク

https://golang.org/ref/spec#Pointer_types
https://tour.golang.org/moretypes/1
https://9cguide.appspot.com/05-01.html
https://9cguide.appspot.com/15-01.html

pylor1n
IT業界1年目、SES勤務。自分の学びを記録するつもりが、記事を書くと何故か説明してる風になっちゃう。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした