【Go言語】例題あり!もう迷わないポインタ解説!!
こんにちは、めんだこです!
今回は、Go言語のポインタについて、ステップごと分けて解説させていただきます。
最後に例題も用意してみました。
ご自身の環境で実際に動かしてみると、より理解が深まると思います!
変数とメモリ空間
var x int32 = 10
var y bool = true
Go言語で宣言されるすべての変数は、連続したアドレス(各メモリの位置を表す数値)を持つメモリの集まり(=メモリ空間)に情報が格納されます。
変数の型によって、必要なメモリの数が異なります。
この例の「x」は、int32型の変数なので、32bit、すなわち4byteの領域を保持します。
「y」は、ture(1)かfalse(0)を表せれば良いため、1bit(→ 1byte)の領域で充分です。
アドレス演算子[&]
ポインターとは、ある値が格納されているメモリのアドレスを保持する変数のことです。
ポインタの宣言には、次の演算子を使います。
- &: アドレス演算子(address operator)
実際に、次のコードを見てみましょう。
var x int32 = 10
var y bool = true
pointerX := &x
pointerY := &y
fmt.Println("pointerX:", pointerX) // output: pointerX: [xのアドレス]
fmt.Println("pointerY:", pointerY) // output: pointerY: [yのアドレス]
アドレス演算子「&」を使って宣言された「pointerX」と「pointerY」は、それぞれ「x」と「y」のポインタ変数です。
それぞれが、ポイントしている変数のアドレスを保持していることが分かります。
先ほどのプログラムを実際の環境で実行してみると、どうなるでしょう。
go run main.go
pointerX: 0x40000a4010
pointerY: 0x40000a4014
私の環境では、このような結果が得られました。
多くの場合、ポインタの値(アドレス)は16進数(0x~)で表現されています。
これを見ると、確かに「x」が4つのアドレス(**010 ~ ** 013)を保持していることがわかりますね。
間接演算子[*]
ポインタ変数がポイントしている変数の値を、間接的に取り出したい場合、次の演算子を使います。
- *: 間接演算子(indirection operator)
実際に次のコードを見てみましょう。
var x int32 = 10
var y bool = true
pointerX := &x
pointerY := &y
fmt.Println(*pointerX) // output: 10
fmt.Println(*pointerY) // output: true
このように、間接演算子[*]を使うことで、間接的にポイントしている値にアクセスすることができます。
この操作をデリファレンシング(dereferencing)と言います。
例題
次のコードの出力を考えてみましょう。
(上記で解説していない部分も含まれます)
例題1
num := 100
pointerNum := &num
*pointerNum++
fmt.Println(num)
- 解答
-
101
numの値を間接演算子[*]を用いて、間接的にインクリメントしています。
そのため、オリジナルの変数numの値も変更されます。
例題2
var x *int
fmt.Println(x == nil)
fmt.Println(*x)
- 解答
-
true
panic: runtime error: invalid memory address or nil pointer dereference
(意訳) 実行時エラー: 無効なメモリ、又はnilポインタをデリファレンスしています。
間接演算子[*]を用いることで、*int型(int型の変数のポインタのみ代入可能)の変数を宣言しています。
変数はx、型は*intです。要注意です。
この変数xには、値が代入されていないため、これはnilとなります。
さらにnilポインタをデリファレンシングしようとすると、参照先が存在しないためpanicエラーを起こします。
例題3
var x = new(int)
fmt.Println(x == nil)
fmt.Println(*x)
- 解答
-
false
0
実は、組み込み関数newでもポインタ変数を宣言できます。
この場合、該当の型のゼロバリュー(int型なら0、string型なら空文字” “)のインスタンスを持つポインターが返却されます。
例題4
var x *string
y := "Hello!"
x = &y
fmt.Println(*x)
- 解答
-
Hello!
変数yを介して、xに文字列型のポインタ値を代入しています。
例題5
var x *string
x = &"Hello"
fmt.Println(*x)
- 解答
-
invalid operation: cannot take address of “Hello” (untyped string constant)
(意訳)無効な操作: “Hello”のアドレスは取得できません(型付けできない文字列定数)。
このように、変数を介さずに文字列のアドレスを取得しようとすると、コンパイル時にエラーになります。
アドレス演算子[&]は、直接プリミティブ型の値、又は定数に対して指定できません。※プリミティブ型: int, float, string, boolなどのGo言語の基本的な型
参考文献
こちらの記事の執筆にあたり、以下のWebサイト・書籍を参考・利用させていただきました。
ありがとうございました!
【Webサイト】
- Effective Go(Google)
- A Tour of Go(Google)
- ChatGPT(OpenAI)
- ポインタ(プログラミング)(Wikipedia)
【書籍】
- Go言語ハンズオン(著者: 掌田 津揶乃, 発行所: 株式会社 秀和システム, 発行日: 2021.03.10)
- Learning Go, 2nd Edition(著者: Jon Bodner, 発行所: O’Reilly Media, Inc., 発行日: 2024.01)
今回は、ご覧いただきありがとうございました。
また、次の記事でお会いしましょう!では!
より良い記事を提供するため、この記事に対するご指摘等ございましたら随時コメントを募集しています。
お気軽に書き込みください。コメントお待ちしております!