変数をポインタ型で宣言するのと普通に宣言するのとでは何が違うの?
はじめに
ポインタ型は誰しもが習うはずですが、意外とそれを意識して開発することがない気がするので改めてまとめておきました。
目次
- 具体的に何が違うか
- 使用したら効果的な場面
具体的に何が違うか
通常の宣言
- 通常の宣言では変数に値を格納します。この場合、値のコピーが作成されるので変数同士の値は共有されず独立されています。例えば:
var a int = 10
var b int = a
b = 20
これらはbはaと値を共有しているわけではなく、bはaをコピーしたものであり、それぞれは独立した関係にあります。
ポインタ型の変数の宣言
- ポインタ型の宣言では変数に値のメモリアドレスを格納します。ポインタ型を用いると、異なる変数間でも参照しているメモリアドレスは同じなので実質的に同じメモリを持っていることになります。例えば:
var a int = 10
var p *int = &a
*p = 20
ここでpはaと同じメモリアドレスを参照しています。つまりこのコードを実施すると*pが変更されるのでaの値と同じようになります。つまりaの値は20に変更されます。
ポインタ型を使用すると何がいいか
- まずポインタ型は独立した変数宣言と違い、コピーを作成するわけではないので効率的なメモリ使用を実現できます。
- データの共有を行うので複数の場所で同じメモリアドレスの値を使用したいけど変数名は変更したいという場面で使用できます
使用したら効果的な場面
- ポインタ型を使用すると上記以外の理由でより効果的な場面をご紹介します。ずばりそれはnilを使用したい場面です。
nilと通常の変数
通常の変数は初期化をされないと自動的にデフォルト値を持たされます。整数なら0が文字列なら空文字""が持たされます。これは変数そのものに値を持たさせるためです。例えば:
var a int
fmt.Println(a) // 出力: 0
このように初期化されていない変数だと自動的にデフォルト値の0が割り当てられます。
nilとポインタ型
ポインタ型で宣言した変数は初期化をされないとメモリアドレスが参照しているメモリがないということになります。その場合、整数でも文字列でもnilが挿入されます。例えば:
var a *int
fmt.Println(&a) // 出力: nil
このように初期化されていないポインタ型の変数だと自動的にnilが割り当てられます。
nilを利用する場面
- これまでに説明したnilの特性を使用すれば簡単にその変数の値が存在するかどうかをチェックできます。
func process(p *int) {
if p == nil {
fmt.Println("パラメータがnilです")
} else {
fmt.Println("値:", *p)
}
}
このように整数や文字列の値が存在するかどうかの判断に0や空文字を使用しなくてもnilを使えば明示的に値の存在の可否を判断できます。これはコードの可読性も向上させます。