この記事は、go language チュートリアルコラムで、Go ポインター (Go Pointer) がどのような状況で使用されるのかを紹介するものです。 !
GOコードでのポインターの使用は、特に使用法のシナリオを区別するのが困難な場合、初心者にはそれほど友好的ではありません。
ポインターを使用する際の最大の誤解の 1 つは、Go のポインターが C 言語のポインターに非常に似ているということだと思います。しかし、そうではありません。 Go では、ポインタは C/C と同じように機能しません。
この記事では、Go ポインターを正しく使用する方法について説明します。
一般に、ポインターを使用すると常に値をコピーする必要がなくなるため、アプリケーションの実行が高速になると考えられています。 Go でも同じ考えがあるのは不思議ではありません。
ただし、Go での ポインターの受け渡しは、通常、値の受け渡し よりも遅くなります。これは、Go がガベージ コレクション言語であることの結果です。関数へのポインタを渡すとき、Go はエスケープ解析を実行して、変数をヒープに格納するかスタックに格納するかを決定する必要があります。これによりすでに余分なオーバーヘッドが追加されていますが、それ以外の場合は変数をヒープに保存できます。変数をヒープに保存すると、GC の実行中に時間もロスします。
Go の便利な機能は、コマンド go build -gcflags="-m"
を実行することで、エスケープ解析がどのように行われたかを確認できることです。これを行うと、Go は変数がヒープにエスケープするかどうかを教えてくれます:
./main.go:44:20: greet ... argument does not escape ./main.go:44:21: greeting escapes to heap ./main.go:44:21: name escapes to heap
変数がヒープにエスケープしない場合、変数はスタック上に残ります。スタックは変数をクリアするためにガベージ コレクターを必要とせず、push/pop
操作のみを実行します。
コンテンツが値によって渡される場合、そのコンテンツは常にスタック上で処理されるため、ガベージ コレクションのオーバーヘッドは発生しません。 (GC はデフォルトで実行されます。ヒープ内のコンテンツが少ないということは、GC が行うべき作業が少ないことを意味します)。
ポインターを使用するとパフォーマンスが低下することがわかりました。必要な場合はポインターを使用しますか?
大規模なデータ構造のコピーポインタのパフォーマンスは常に値の転送より悪いのでしょうか?明らかにそうではありません。大規模なデータ構造を扱う場合、ポインタが役に立ちます。これにより、ガベージ コレクションのコストが大量のデータのコピーのコストで相殺される可能性があります。 これについて話すと、「そのビッグデータはどのくらいの大きさにすべきですか?」といつも尋ねられます。 ここに固定値はないと思いますが、パフォーマンスに関連するものはすべてベンチマークする必要があります。 Go には強力なベンチマーク ツールが組み込まれており、これを最大限に活用できます。変数関数のパラメーターを変更する唯一の方法は、ポインターを渡すことです。デフォルトでは、値の変更はコピーに対して実行されます。したがって、これらの変更は、それを呼び出す関数に反映できません。 次のコードを見てください:type person struct { name string }func main() { p := person{"Richard"} rename(p) fmt.Println(p) }func rename(p person) { p.name = "test" }person への変更はそのコピーに対して実行されるため、出力は
Richard になります。基になる人物オブジェクトの値を変更する場合は、ポインターを使用する必要があります。
func main() { p := person{"Richard"} rename(&p) fmt.Println(p) }func rename(p *person) { p.name = "test" }上記と同様に、
testを出力します。可変性とは、Go でポインターが使用される状況です。これが良いことなのかどうかは議論の余地がある。
func (p *person) rename(s string) { p.name = s }func (p *person) printName() { fmt.Println(p.name) }は
func (p *person) rename(s string) { p.name = s }func (p person) printName() { fmt.Println(p.name) }よりも優れていますが、一貫性のために
printName でポインターを使用する必要はありません。ただし、これにより API が簡素化され、参照が必要な場所を正確に覚えておく必要がなくなります。
nil ポインターであり、値が設定されていないことを意味します。この要件は、次のように実装することもできます。
type exam struct { score int present bool }別の
present フィールドを使用して、学生が試験を受けなかったことを示します。
Go では可能な限りデフォルト値を使用することが合理的であると考えています。これはすべての状況で機能するわけではありませんが、私の場合は大きな事故を防ぐことができました。ポインターの代わりに値を使用すると、NULL ポインターによるトニー・ホアの「100 万ドルの間違い」は発生しません。
デフォルト値のゼロは、多数の宣言を避けるのに役立ちます。
另一个好处是易变性造成的问题比它解决的问题多的得多。易变性给函数带来的副作用同时使得调试变得更加困难。 通过让函数返回修改之后的结构体,可以避免这种突变。
重写之前的例子
func main() { p := person{"richard"} p = rename(p) fmt.Println(p) }func rename(p person) person { p.name = "test" return p }
这也是 append
如何工作的,所以并不陌生。
x := []int{1,2} x = append(x, 3) x = append(x, 4)
鉴于指针的安全性,和值处理比指针处理更快,使用指针需要反复斟酌。
原文地址:https://medium.com/@meeusdylan/when-to-use-pointers-in-go-44c15fe04eac
译文地址:https://learnku.com/go/t/60923
以上がGo ポインターをいつ使用するか知っていますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。