ポインターの役割
-
メモリの効率的な操作:
ポインターを使用することで、大きなデータ構造をコピーする代わりに、そのデータ構造のメモリアドレスをコピーするだけで済みます。これにより、メモリ使用量と処理速度の面で効率が向上します。 -
参照渡し:
関数に引数を渡す際に、値渡しではなく参照渡しを行うことができます。これにより、関数内で元の変数の値を直接変更することが可能になります。 -
動的メモリアロケーション:
ポインターを使用することで、動的にメモリを割り当てたり解放したりすることができます。Goでは、new
やmake
を使用して動的にメモリを割り当てることができます。
ポインターの基本的な使い方
ポインターの宣言
ポインターは、*
を使って宣言します。例えば、*int
はint
型のポインターです。
var p *int
ポインターの初期化
変数のアドレスを取得するには、&
演算子を使用します。
var i int = 42
p = &i
これで、p
はi
のメモリアドレスを保持します。
ポインターのデリファレンス
ポインターが指す値を取得するには、*
演算子を使用します。
fmt.Println(*p) // 42
*p
はp
が指すアドレスの値を取得します。
ポインターを使用した関数呼び出し
ポインターを使って関数に変数を渡すことで、その変数の値を関数内で変更することができます。
func increment(p *int) {
*p++
}
func main() {
var x int = 10
increment(&x)
fmt.Println(x) // 11
}
この例では、increment
関数はポインターを受け取り、そのポインターが指す値をインクリメントしています。
ポインターの詳細な例
以下に、ポインターの詳細な使用例を示します。
package main
import (
"fmt"
"unsafe"
)
func main() {
var i int = 10
var p *int = &i
fmt.Printf("i: %v\n", i) // 10
fmt.Printf("p: %p\n", p) // メモリアドレス
fmt.Printf("*p: %v\n", *p) // 10
// pを使ってiの値を変更
*p = 20
fmt.Printf("i after *p = 20: %v\n", i) // 20
// ポインターサイズ
fmt.Printf("size of p: %d bytes\n", unsafe.Sizeof(p)) // 8 (64ビット環境では一般的に8バイト)
// 二重ポインター
var pp **int = &p
fmt.Printf("pp: %p\n", pp) // ポインターpのアドレス
fmt.Printf("*pp: %p\n", *pp) // pの値(iのアドレス)
fmt.Printf("**pp: %v\n", **pp) // 20
// 関数を使ったポインター操作
changeValue(p)
fmt.Printf("i after changeValue: %v\n", i) // 30
}
func changeValue(p *int) {
*p = 30
}