0
1
テーマ、知りたいこと
実際の開発において、Goのポインタとはいつどのような場面で使えばいいのか知りたいです。
背景、状況
私はJavaからプログラミングの学習を始めた者で、Java歴は1年ちょっとです。
最近Goを勉強しているのですが、Goでのポインタを実際の開発において、具体的にどのような場面で使うべきなのかがあまりイメージが湧きません。
Goのポインタの具体的な使用例、実務ではこういう場面で使うことが多いよ!などの意見があれば教えて欲しいです。
宜しくお願いします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答6件
#1
総合スコア7899
投稿2023/12/12 19:13
とりあえずこの記事とか参考になるのでは。
https://zenn.dev/uji/articles/f6ab9a06320294146733
あと、できれば実際に書いたコードとか、どんな資料を使って勉強してるとか、Go を使って (Go に限らず) どんなプログラムを作りたいのかなど、もっと具体的に質問してもらえると答えやすくなると思います。
#2
総合スコア4443
投稿2023/12/17 03:05
編集2023/12/17 03:24特定の言語について学ぶのならその言語の仕様書を読むのが一番いいのです。仕様書にはその言語のことが過不足なくすべて書いてあります。あと、仕様書を読めるようになると他の言語を勉強するのも簡単になります。仕様書を見比べればすでに学んだ言語と違う点がすぐわかりますから。
いきなり仕様書を読むのは大変そうだということなら、その言語の作者が書いた本 を読むのが次善の方法です。Goなら『プログラミング言語Go』ですね。最初から全部を読む必要はないです。買って最初の数章を読んだら手元に置いておき、何か疑問が出てきたら目次を見て関係ありそうな箇所を読んでみましょう。
[訂正] 本の原作者のカーニハンはGoの作者ではないですね。でも作者たちと長年一緒に仕事しているので作者みたいなものです。
前置き終わり。
JavaでもGoでもそうなのですが、関数への引数の渡しかたには「値渡し」(「値呼び (call by value)」とも言う) しかありません。値渡しというのは、関数に引数を引き渡すときにいちいちコピーを作って渡すやりかたです [1]。
プリミティブ型 (基本データ型) の引数なら特に問題はないです。コピーした値は元の値と全く同じものです。整数1
と、それをコピーした整数1
は区別がつきません。
これがコンポジット型だと問題になりえます。あまりいい例が作れなかったのですが、こういうコードがあったとします。
go
1package main 2import "fmt" 3 4type Container struct { 5 Field int 6} 7 8func CountUp(c Container) { 9 c.Field++ 10} 11 12func main() { 13 c := Container{} 14 CountUp(c) 15 fmt.Println(c) 16}
Container
という構造体型にはField
という整数型のフィールドがあります。CountUp
という関数はContainer型の引数を受け取って、Field
の値をひとつ増やします。……というつもりのコードです。しかし実行してみると
{0}
となり、フィールドの値が増えていません。
これは、値渡しによって引数c
の値がコピーされたからです。CountUp()
の内部ではmain()
から渡された引数のコピーに対してフィールドの更新をしていますから、main()
の側ではc
のフィールドは変更されません。
ここでポインタを使ってみます。変数のポインタというのは「その変数のアドレス」を表します。つまり、変数の値が格納されているコンピュータのメモリ上の位置を表す数値です。
go
1package main 2import "fmt" 3 4type Container struct { 5 Field int 6} 7 8func CountUp(c *Container) { 9 c.Field++ 10} 11 12func main() { 13 c := Container{} 14 CountUp(&c) 15 fmt.Println(c) 16}
CountUp()
がポインタを引数に取るように変更しました。c
へのポインタを値渡しするので、c
そのものはコピーされません。CountUp()
の内部からはポインタを通じてコピーではないc
自身のフィールドを操作できます [2]。結果は次のようになります。
{1}
最初に「Javaも値渡しだ」と述べましたが、Javaでなぜ同じことが起きないかというと、Javaでは参照型 (プリミティブ型でない型) の値は参照だからです。「参照」というのはポインタみたいなものだと思ってください。値渡しでコピーされるのは参照であってオブジェクトそのものの内容ではありません。つまりJavaではなんにも考えなくてもGoでポインタを使っているのと同じことをやっています。
同様に、メソッドのレシーバも値渡しされます。ですからレシーバが構造体そのものか構造体のポインタかで動作が変わってきます。上のCountUp()
をメソッドとして実装したとすると
go
1func (c Container) CountUp() { 2 ... 3} 4 5または 6 7func (c *Container) CountUp() { 8 ... 9}
前者ではCountUp()
内部からc
のフィールドを変更できません。後者では変更できます。ですから、オブジェクトの変更を許すのならポインタをレシーバにすべきですし、逆に変更されないことを保証するためにあえてポインタを使わないということも考えられます。
他にもポインタを使う・使わない場面というのはあると思いますが、ひとまず例を挙げました。
[1] たまに「Javaでは値渡しと参照渡しがある」という説明をしている人がいますが、後の説明の通り、これは厳密には間違いです。
[2] c
がポインタなら c.Field++
ではなく (*c).Field++
と書かないといけないのでは……と思うかもしれません。確かに後者が正確な書きかたですが、略記法として前者の書きかたもできます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
#3
この回答は、運営により削除されました。
#4
この回答は、運営により削除されました。
#5
この回答は、運営により削除されました。
#6
この回答は、運営により削除されました。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。