Goのポインターは関数の引数の変更を許可することを知っていますが、(適切なconstまたはmutable修飾子を使用して)参照のみを採用した方が簡単ではなかったでしょう。これで、ポインターが用意され、マップやチャネルなどの一部の組み込み型については、暗黙的に参照渡しされます。
何かが足りないのですか、それともGoのポインターが不必要な複雑なものなのですか?
Goのポインターは関数の引数の変更を許可することを知っていますが、(適切なconstまたはmutable修飾子を使用して)参照のみを採用した方が簡単ではなかったでしょう。これで、ポインターが用意され、マップやチャネルなどの一部の組み込み型については、暗黙的に参照渡しされます。
何かが足りないのですか、それともGoのポインターが不必要な複雑なものなのですか?
回答:
私はhttp://www.golang-book.com/8から取られた例が本当に好きです
func zero(x int) {
x = 0
}
func main() {
x := 5
zero(x)
fmt.Println(x) // x is still 5
}
と対照的に
func zero(xPtr *int) {
*xPtr = 0
}
func main() {
x := 5
zero(&x)
fmt.Println(x) // x is 0
}
ポインタはいくつかの理由で役立ちます。ポインタを使用すると、メモリレイアウトを制御できます(CPUキャッシュの効率に影響します)。Goでは、すべてのメンバーが連続したメモリにある構造を定義できます。
type Point struct {
x, y int
}
type LineSegment struct {
source, destination Point
}
この場合、Point
構造は構造内に埋め込まれますLineSegment
。ただし、常にデータを直接埋め込むことはできません。バイナリツリーやリンクリストなどの構造をサポートする場合は、ある種のポインタをサポートする必要があります。
type TreeNode {
value int
left *TreeNode
right *TreeNode
}
Java、Pythonなどでは、複合型を埋め込むことができないため、この問題はありません。したがって、埋め込みとポインティングを構文的に区別する必要はありません。
同じことを達成するための可能な代替策は、C#とSwiftがそうするように、struct
とを区別するclass
ことです。しかし、これには制限があります。通常、関数が構造体をinout
パラメーターとして受け取ることを指定して、構造体のコピーを回避できますが、構造体への参照(ポインター)を格納することはできません。これは、たとえばプールアロケーターを作成する(以下を参照)場合など、構造体を参照型として扱うことができないことを意味します。
ポインタを使用して、独自のプールアロケータを作成することもできます(これは、原則を示すために多数のチェックを削除して非常に簡略化されています)。
type TreeNode {
value int
left *TreeNode
right *TreeNode
nextFreeNode *TreeNode; // For memory allocation
}
var pool [1024]TreeNode
var firstFreeNode *TreeNode = &pool[0]
func poolAlloc() *TreeNode {
node := firstFreeNode
firstFreeNode = firstFreeNode.nextFreeNode
return node
}
func freeNode(node *TreeNode) {
node.nextFreeNode = firstFreeNode
firstFreeNode = node
}
ポインタを使用すると、を実装することもできますswap
。それは2つの変数の値を交換しています:
func swap(a *int, b *int) {
temp := *a
*a = *b
*b = temp
}
メモリのレイアウトと使用を制御する機能がないために、パフォーマンスを同じ範囲に調整できないため(キャッシュミスはパフォーマンスに大きく影響します)、JavaなどのシステムプログラミングでC ++を完全に置き換えることはできませんでした。Goは多くの領域でC ++を置き換えることを目的としており、そのためポインターをサポートする必要があります。
参照は再割り当てできませんが、ポインターは再割り当てできます。これだけでも、参照を使用できない多くの状況でポインタが役立ちます。
Goは、簡潔でシンプルな言語になるように設計されています。したがって、それは値とポインタだけで始まりました。その後、必要に応じて、いくつかの参照タイプ(スライス、マップ、チャネル)が追加されました。
Goプログラミング言語:言語設計に関するFAQ:配列が値であるのに、マップ、スライス、チャネルが参照されるのはなぜですか?
「そのトピックには多くの歴史があります。初期の段階では、マップとチャネルは構文的にポインターであり、非ポインターインスタンスを宣言したり使用したりすることは不可能でした。また、配列がどのように機能するかについて苦労しました。最終的に、厳密な分離を決定しましたポインタと値の組み合わせにより、言語が使いにくくなりました。配列の参照形式を処理するためのスライスを含む参照型を導入することで、これらの問題が解決されました。参照型は、言語にいくつかの残念な複雑さを追加しますが、使いやすさに大きな影響を与えます。彼らが導入されたとき、より生産的で快適な言語です。」
高速コンパイルは、Goプログラミング言語の主要な設計目標です。それにはコストがかかります。犠牲者の1つは、変数(基本的なコンパイル時定数を除く)とパラメーターを不変としてマークする機能です。要求されたが断られた。
golang-nuts:言語を使います。いくつかのフィードバックと疑問。
「型システムにconstを追加すると、どこにでも強制的に表示され、何かが変更された場合は強制的に削除されます。何らかの方法でオブジェクトを不変にマークすることにはいくつかの利点があるかもしれませんが、const型修飾子はそうではないと思いますトーゴ。"