ポインタを理解せよ!(Go言語)
本日は,Goのポインタについてです!
最初らへんの投稿で,「ポインタは無いです!」とか言っていたのですが,
Goにポインタはありますm(__)m
ただし,ポインタ演算は現時点でありません!
今回は,
とりあえずポインタを理解する!
っていうテーマでやっていきます!
それでは~Go!
説明用のコード
本日の説明用のコードはこちらです!
package main
import "fmt"
func main() {
var p *int /* ポインタは,アドレスを格納する変数 */
if nil == p { /* ポインタの初期値はnil */
fmt.Println(" p is nil : ", p)
}
i := 10
p = &i
fmt.Println(" p is ", p)
fmt.Println("*p is ", *p)
input100(&i) /* ポインタの使い方①:関数の中で,呼び出し元の変数の値を操作する */
fmt.Println(" i is ", i)
}
func input100(v *int) {
*v = 100
}
上記のコードは,下記サイトで実行できます.
ポインタって何?
ポインタについては,下記サイトを参照してください.
って言いたかったのですが,Wiki...難しい(; ・`д・´)
ということで簡単に解説します.ただし,ポインタの概要を理解してもらうために,正確性はオミッションしておりますm(__)m
まず,こちらの図をご覧ください.
いきなりよく分からん「仮想メモリ」という言葉が出てきますが,
「プログラムから見たときの,メモリ(作業机)」
と解釈してください。
この仮想メモリは,基本的に,他のプログラム(プロセス)からはアクセスできません.
この仮想メモリは,
Address(住所)とData(データ)
から構成されています.
ここで,「var i int =2」のように整数型の変数を宣言し,値2で初期化するプログラムが実行されると,
OSが適当な番地(ここでは,1番地)に値2を格納します.
次に,「p := &i」のようにポインタ型の変数pが,iのアドレス(ここでは,1番地)で初期化されると,またも,
OSが適当な番地(ここでは,3番地)に値1を格納します.
この値1は変数iのアドレスとなります.
なお,「&i」でアンパサンドが出てきましたが,
これは,変数iのアドレスを意味します.
つまり,ポインタとは何かと言うと
「アドレス」を格納するための変数です.
次に,こちらをご覧ください.
変数iのアドレスを格納したポインタ変数pに対して,「*p」のようにアスタリスクをつけると,
pが保持しているアドレスのデータにアクセスすることができるのです!
つまり,この説明では「*p」は「2」を意味します.
ここで,さらに「*p=3」のようなプログラムが実行されると,
アドレスが1番地のデータが2から3に上書きされます.
つまり,変数iのデータが上書きされるのです!
これが,ポインタです!
Goでは,上記内容を理解しておけばOKです.
他の言語では,上記以外にポインタ演算という強力な仕組みがあるのですが,これはバグの温床になりやすいと言われています.
ですので,Goでは,ポインタ演算は導入されていないようですね.
ポインタの具体例
ここまで,聞くと,「何のためにポインタが存在すんねん!どうやって使うねん!」と突っ込みたくなりますよね....その気持ちよくわかります(*´ω`*)
今回は,関数にポインタを渡す例を紹介します.
とその前に,説明用のソースコードを,ちょこっと解説いたします.
まず,このように
var p *int /* ポインタは,アドレスを格納する変数 */
if nil == p { /* ポインタの初期値はnil */
fmt.Println(" p is nil : ", p)
}
ポインタを宣言すると,Goのポインタの初期値には,「nil」が格納されます.
そして,
i := 10
p = &i
fmt.Println(" p is ", p)
fmt.Println("*p is ", *p)
このように,int型の変数iに10を格納し,int型変数のポインタpに変数iのアドレスを代入します.
そして,
ポインタpの値と,その値(変数iのアドレス)が示すデータ
を参照すると,
p is 0xc0000140b8
*p is 10
のような値が返されます.(アドレスは,このような感じで16進数で表されます...上の説明はかなり簡略化されてますw)
そして,「*p」は,変数iの値が参照されてますね!
いよいよ,「関数にポインタを渡す具体例」です!
input100(&i) /* ポインタの使い方①:関数の中で,呼び出し元の変数の値を操作する */
fmt.Println(" i is ", i)
}
func input100(v *int) {
*v = 100
}
上記では,関数input100がint型のポインタを受け取り,そのポインタが保持するアドレスのデータ領域に100を書き込んでいます.
このように,関数の引数にポインタを使うことで,
呼びたし元(ここでは,main関数)からアクセスできる変数(ここでは,変数i)に関数で処理した結果を格納することができます!
もし,関数input100が
func input100(v int) {
v = 100
}
のように引数が「int型の変数」であった場合,「変数i」の値は変更されません!(ぜひ,試してみてください.)
今回の例(関数の引数にポインタ)では,あまり,ポインタのご利益が感じにくいかと思いますが,今後紹介する構造体,配列,mapなどでは,ポインタの威力が実感できるかと思います.
現時点では,ポインタっていう仕組みがあるんだなぁーって感じでOKです.
それでは,次回で会いましょう('ω')ノ
サポートお願いいたします.主に初心者から中級向けに,ソフトウェア開発に関する知識を提供していきます.