Go'da işaretçilerin olmasının anlamı nedir?


100

Go'daki işaretçilerin bir işlevin argümanlarının mutasyonuna izin verdiğini biliyorum, ancak yalnızca referansları (uygun sabit veya değiştirilebilir niteleyicilerle) benimsemeleri daha kolay olmaz mıydı. Artık işaretçilerimiz var ve haritalar ve kanallar gibi bazı yerleşik türler için başvuruya göre örtük geçiş var.

Bir şey mi kaçırıyorum yoksa Go'daki işaretçiler sadece gereksiz bir komplikasyon mu?


1
İşte açıklığa kavuşturmaya yardımcı olabilecek bir soru: stackoverflow.com/questions/795160/… Referansları değere göre geçirmekle gerçekten referansla geçirmek arasında bir fark vardır.
R. Martinho Fernandes

1
Not: Soru Java ile ilgilidir, ancak burada da geçerlidir.
R. Martinho Fernandes

1
"ve haritalar ve kanallar gibi bazı yerleşik türler için başvuruya göre örtük geçiş." Hayır, Go'da her şey değer bazındadır. Bazı türler (gayri resmi olarak tanımlanır) referans türleridir, çünkü dahili değiştirilebilir durumları vardır.
newacct

Bu sorunun sorunu, "referansların" iyi tanımlanmış özelliklere sahip tek bir şey olmamasıdır. "Referanslar" terimi çok belirsizdir. Cevaplarda kaç kişinin "referanslar" kelimesine farklı şeyler okuduğunu görebiliriz. Dolayısıyla bu soru, Go işaretçileri ile sorunun aklındaki referanslar arasında tam olarak ne gibi farklılıklar olduğunu detaylandırmalıdır.
mtraceur

Yanıtlar:


37

Http://www.golang-book.com/8 adresinden alınan örneği gerçekten seviyorum

func zero(x int) {
    x = 0
}
func main() {
    x := 5
    zero(x)
    fmt.Println(x) // x is still 5
}

aksine

func zero(xPtr *int) {
    *xPtr = 0
}
func main() {
    x := 5
    zero(&x)
    fmt.Println(x) // x is 0
}

43
Soru, "neden referanslar yerine işaretçilerimiz var " idi ve bu örneğin neden referanslarla çalışmadığını anlamıyorum.
AndreKR

@AndreKR Çünkü referansla mı yoksa değere göre mi geçeceğimizi seçebiliriz. Her ikisinin de arzu edilebileceği bazı durumlar vardır.
JDSweetBeat

10
@DJMethaneMan Bu "referanslara karşı işaretçiler", "değere göre işaretçiler vs." değil!
AndreKR

Yan yorum olarak, referansa göre geçiş C # 2.0'da "ref" anahtar sözcüğü aracılığıyla eklendi. Elbette bazı durumlarda işaretçiler daha kullanışlıdır, çünkü işaretçiyi gösteren işaretçiye sahip olabiliriz ...
robbie fan

Go'nun en kolay popüler dillerden biri olması gerektiğini anlamıyorum ve yine de böyle bir "özellik" içeriyorlar ... Bu kafa karıştırıcı ve gereksiz görünüyor, en azından burada bunu işaret eden insanlar için.
Akito

34

İşaretçiler birkaç nedenden dolayı faydalıdır. İşaretçiler bellek düzeni üzerinde kontrole izin verir (CPU önbelleğinin verimliliğini etkiler). Go'da, tüm üyelerin bitişik bellekte olduğu bir yapı tanımlayabiliriz:

type Point struct {
  x, y int
}

type LineSegment struct {
  source, destination Point
}

Bu durumda Pointyapılar yapının içine gömülüdür LineSegment. Ancak verileri her zaman doğrudan gömemezsiniz. İkili ağaçlar veya bağlantılı liste gibi yapıları desteklemek istiyorsanız, bir tür işaretçi desteklemeniz gerekir.

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode
}

Java, Python vb. Bu soruna sahip değildir çünkü kompozit türleri yerleştirmenize izin vermez, bu nedenle gömme ve işaretleme arasında sözdizimsel olarak ayrım yapmaya gerek yoktur.

Go işaretçileriyle çözülen Swift / C # yapılarıyla ilgili sorunlar

Aynısını başarmanın olası bir alternatifi , C # ve Swift'in yaptığı gibi structve arasında ayrım classyapmaktır. Ancak bunun sınırlamaları var. Yapının inoutkopyalanmasını önlemek için genellikle bir işlevin parametre olarak bir yapı aldığını belirtebilirsiniz , ancak yapılara referansları (işaretçiler) depolamanıza izin vermez. Bu, bir yapıyı, örneğin bir havuz ayırıcısı oluşturmak için yararlı bulduğunuzda asla referans türü olarak ele alamayacağınız anlamına gelir (aşağıya bakın).

Özel Bellek Ayırıcı

İşaretçileri kullanarak kendi havuz ayırıcınızı da oluşturabilirsiniz (bu, yalnızca prensibi göstermek için birçok denetim kaldırılarak çok basitleştirilmiştir):

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
}

İki değeri değiştirin

İşaretçiler ayrıca uygulamanıza izin verir swap. Bu, iki değişkenin değerlerini değiştirmektir:

func swap(a *int, b *int) {
   temp := *a
   *a = *b
   *b = temp
}

Sonuç

Java, Google gibi yerlerde sistem programlaması için C ++ 'ın yerini hiçbir zaman tam olarak değiştiremedi, çünkü kısmen, bellek düzenini ve kullanımını kontrol etme becerisinin olmaması nedeniyle performans aynı ölçüde ayarlanamıyor (önbellek kayıpları performansı önemli ölçüde etkiler). Go, birçok alanda C ++ 'ın yerini almayı hedefledi ve bu nedenle işaretçileri desteklemesi gerekiyor.


7
C # yapıların referans olarak aktarılmasına izin verir. "Ref" ve "out" anahtar kelimelerine bakın.
olegz

1
Tamam öyleyse Swift gibi. Örneğimi güncellemenin bir yolunu düşüneceğim.
Erik Engheim

29

İşaretçiler atanabiliyorken referanslar yeniden atanamaz. Bu tek başına işaretçileri referansların kullanılamadığı birçok durumda kullanışlı kılar.


17
Referansların yeniden atanabilir olup olmadığı, dile özgü bir uygulama sorunudur.
crantok

28

Go, kısa ve minimalist bir dil olarak tasarlandı. Bu nedenle, sadece değerler ve işaretlerle başladı. Daha sonra zorunlu olarak bazı referans türleri (dilimler, haritalar ve kanallar) eklenmiştir.


Go Programlama Dili: Dil Tasarımı SSS: Diziler değer iken neden haritalar, dilimler ve kanal referansları?

"Bu konuda çok fazla tarih var. Daha önce, haritalar ve kanallar sözdizimsel olarak işaretçilerdi ve işaretçi olmayan bir örneği bildirmek veya kullanmak imkansızdı. Ayrıca, dizilerin nasıl çalışması gerektiğiyle uğraştık. Sonunda kesin ayrıma karar verdik işaretçiler ve değerler dilin kullanımını zorlaştırdı. Dizilerin referans biçimini ele almak için dilimler dahil olmak üzere referans türlerinin tanıtılması bu sorunları çözdü. Referans türleri dile biraz üzücü bir karmaşıklık katsa da kullanılabilirlik üzerinde büyük bir etkiye sahiptir: Git tanıtıldıklarında daha üretken, daha rahat bir dil. "


Hızlı derleme, Go programlama dilinin ana tasarım hedefidir; maliyeti var. Kayıplardan biri, değişkenleri (temel derleme süresi sabitleri hariç) ve parametreleri değişmez olarak işaretleme yeteneği gibi görünmektedir. İstendi, ancak reddedildi.


golang-fındık: go dil. Bazı geri bildirimler ve şüpheler.

"Yazım sistemine const eklemek onu her yerde görünmeye zorlar ve bir şey değişirse kişiyi her yerde kaldırmaya zorlar. Nesneleri bir şekilde değişmez olarak işaretlemenin bir faydası olsa da, bir const türü niteleyicinin işe yarayacağını düşünmüyoruz. gitmek."


FWIW, Go'daki "referans türleri" de yeniden atanabilir. Daha çok örtük işaretçiler gibi mi?
Matt Joiner

1
İşaretçi (ve uzunluk, kapasite, ...) içeren yapılar için özel sözdizimleridir.
mk12
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.