見出し画像

ポインタを理解せよ!(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

まず,こちらの図をご覧ください.

画像1

いきなりよく分からん「仮想メモリ」という言葉が出てきますが,
  「プログラムから見たときの,メモリ(作業机)」
と解釈してください。
この仮想メモリは,基本的に,他のプログラム(プロセス)からはアクセスできません.

この仮想メモリは,
 Address(住所)とData(データ)
から構成されています.

ここで,「var i int =2」のように整数型の変数を宣言し,値2で初期化するプログラムが実行されると,
 OSが適当な番地(ここでは,1番地)に値2を格納します.

次に,「p := &i」のようにポインタ型の変数pが,iのアドレス(ここでは,1番地)で初期化されると,またも,
OSが適当な番地(ここでは,3番地)に値1を格納します.
この値1は変数iのアドレスとなります.
なお,「&i」でアンパサンドが出てきましたが,
 これは,変数iのアドレスを意味します.

つまり,ポインタとは何かと言うと
 「アドレス」を格納するための変数です.

次に,こちらをご覧ください.

画像2

変数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です.

それでは,次回で会いましょう('ω')ノ

ここから先は

0字
500円で,本マガジンのすべてを読むことができます.プログラム初心者でもGO言語を習得できるように丁寧な説明を心がけています.もちろん,購読できない方にも,Goの魅力を伝えていきます!

基本的にGoの公式サイトをわかり易く解説していきます.本マガジンは500円です.購入すれば,本マガジン内の全記事がストレスなく読めます!

期間限定 PayPay支払いすると抽選でお得に!

サポートお願いいたします.主に初心者から中級向けに,ソフトウェア開発に関する知識を提供していきます.