อะไรคือจุดที่มีตัวชี้ใน Go?


102

ฉันรู้ว่าพอยน์เตอร์ใน Go อนุญาตให้มีการกลายพันธุ์ของอาร์กิวเมนต์ของฟังก์ชัน แต่จะไม่ง่ายไปกว่านี้หรือไม่หากพวกเขาใช้การอ้างอิงเพียงอย่างเดียว (โดยมี const ที่เหมาะสมหรือคุณสมบัติที่เปลี่ยนแปลงได้) ตอนนี้เรามีตัวชี้และสำหรับบางประเภทในตัวเช่นแผนที่และช่องสัญญาณโดยปริยายโดยการอ้างอิง

ฉันพลาดอะไรไปหรือเป็นคำแนะนำใน Go เป็นเพียงภาวะแทรกซ้อนที่ไม่จำเป็น?


1
นี่คือคำถามที่อาจช่วยชี้แจง: stackoverflow.com/questions/795160/…มีความแตกต่างระหว่างการส่งการอ้างอิงตามค่าและการส่งต่อโดยอ้างอิงอย่างแท้จริง
R. Martinho Fernandes

1
หมายเหตุ: คำถามเกี่ยวกับ Java แต่ก็ใช้ได้เช่นกัน
R. Martinho Fernandes

1
"และสำหรับบางประเภทในตัวเช่นแผนที่และช่องโดยปริยายโดยการอ้างอิง" ไม่ทุกอย่างเป็นแบบ pass-by-value ใน Go บางประเภทเป็นประเภทอ้างอิง (อธิบายอย่างไม่เป็นทางการ) เนื่องจากมีสถานะที่เปลี่ยนแปลงได้ภายใน
newacct

ปัญหาของคำถามนี้คือ "การอ้างอิง" ไม่ใช่สิ่งเดียวที่มีคุณสมบัติที่กำหนดไว้อย่างดี คำว่า "การอ้างอิง" นั้นคลุมเครือมาก เราจะเห็นได้จากคำตอบว่ามีกี่คนที่อ่านคำว่า "อ้างอิง" ต่างกัน ดังนั้นคำถามนี้ควรอธิบายอย่างละเอียดว่าความแตกต่างระหว่างตัวชี้ Go กับการอ้างอิงของคำถามมีอะไรบ้าง
mtraceur

คำตอบ:


36

ชอบตัวอย่างที่นำมาจาก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
}

47
คำถามคือ "เหตุใดเราจึงมีพอยน์เตอร์แทนการอ้างอิง " และฉันไม่เข้าใจว่าทำไมตัวอย่างนี้จึงใช้ไม่ได้กับการอ้างอิง
AndreKR

@AndreKR เพราะเราสามารถเลือกได้ว่าจะส่งต่อโดยอ้างอิงหรือส่งผ่านค่า มีบางกรณีที่ทั้งสองสามารถเป็นที่ต้องการได้
JDSweetBeat

13
@DJMethaneMan มันคือ "พอยน์เตอร์เทียบกับการอ้างอิง" ไม่ใช่ "พอยน์เตอร์เทียบกับพาส - บาย - ค่า"!
AndreKR

ในฐานะความคิดเห็นด้านข้างได้มีการเพิ่ม pass-by-reference ใน C # 2.0 ผ่านคำหลัก "ref" แน่นอนว่าพอยน์เตอร์ยังสะดวกกว่าในบางกรณีเพราะเราสามารถมีพอยน์เตอร์เพื่อชี้ไปยังพอยน์เตอร์ได้ ...
robbie fan

ฉันไม่เข้าใจว่า Go ควรจะเป็นหนึ่งในภาษายอดนิยมที่ง่ายที่สุด แต่ก็มี "คุณลักษณะ" เช่นนี้ ... มันสับสนและดูเหมือนไม่จำเป็นอย่างน้อยก็มีคนชี้ให้เห็นที่นี่
Akito

34

คำชี้มีประโยชน์ด้วยเหตุผลหลายประการ ตัวชี้ช่วยให้สามารถควบคุมเค้าโครงหน่วยความจำ (มีผลต่อประสิทธิภาพของแคช CPU) ใน Go เราสามารถกำหนดโครงสร้างที่สมาชิกทั้งหมดอยู่ในหน่วยความจำที่ต่อเนื่องกัน:

type Point struct {
  x, y int
}

type LineSegment struct {
  source, destination Point
}

ในกรณีนี้Pointโครงสร้างที่ฝังตัวอยู่ภายในLineSegmentstruct แต่คุณไม่สามารถฝังข้อมูลโดยตรงได้เสมอไป หากคุณต้องการสนับสนุนโครงสร้างเช่นต้นไม้ไบนารีหรือรายการที่เชื่อมโยงคุณต้องสนับสนุนตัวชี้บางประเภท

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

Java, Python และอื่น ๆ ไม่มีปัญหานี้เนื่องจากไม่อนุญาตให้คุณฝังประเภทคอมโพสิตดังนั้นจึงไม่จำเป็นต้องแยกความแตกต่างระหว่างการฝังและการชี้ทางไวยากรณ์

ปัญหาเกี่ยวกับโครงสร้าง Swift / C # แก้ไขได้ด้วยตัวชี้ Go

ทางเลือกอื่นที่เป็นไปได้ในการทำเช่นเดียวกันคือการแยกความแตกต่างระหว่างstructและclassตามที่ C # และ Swift ทำ แต่สิ่งนี้มีข้อ จำกัด ในขณะที่คุณสามารถระบุได้ว่าฟังก์ชันใช้โครงสร้างเป็นinoutพารามิเตอร์เพื่อหลีกเลี่ยงการคัดลอกโครงสร้าง แต่ก็ไม่อนุญาตให้คุณจัดเก็บการอ้างอิง (พอยน์เตอร์) ไปยังโครงสร้าง ซึ่งหมายความว่าคุณไม่สามารถถือว่าโครงสร้างเป็นประเภทอ้างอิงได้เมื่อคุณพบว่ามีประโยชน์เช่นการสร้างตัวจัดสรรพูล (ดูด้านล่าง)

Custom Memory Allocator

การใช้พอยน์เตอร์คุณยังสามารถสร้างตัวจัดสรรพูลของคุณเองได้ (ซึ่งง่ายมากด้วยการลบเช็คจำนวนมากเพื่อแสดงหลักการ):

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ได้ นั่นคือการแลกเปลี่ยนค่าของสองตัวแปร:

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

สรุป

Java ไม่สามารถแทนที่ C ++ ได้อย่างสมบูรณ์สำหรับการเขียนโปรแกรมระบบในที่ต่างๆเช่น Google ส่วนหนึ่งเป็นเพราะไม่สามารถปรับแต่งประสิทธิภาพให้เป็นส่วนขยายเดียวกันได้เนื่องจากขาดความสามารถในการควบคุมรูปแบบหน่วยความจำและการใช้งาน (การพลาดแคชจะส่งผลต่อประสิทธิภาพอย่างมาก) Go มีเป้าหมายที่จะแทนที่ C ++ ในหลาย ๆ ด้านดังนั้นจึงจำเป็นต้องสนับสนุนพอยน์เตอร์


8
C # อนุญาตให้ส่งผ่านโครงสร้างโดยการอ้างอิง ดูคีย์เวิร์ด "ref" และ "out"
olegz

1
โอเคมันก็เหมือนกับ Swift ฉันจะคิดหาวิธีอัปเดตตัวอย่างของฉัน
Erik Engheim

30

Go ถูกออกแบบมาให้ใช้ภาษาที่เรียบง่ายและสั้น มันจึงเริ่มต้นด้วยค่านิยมและตัวชี้ ต่อมาตามความจำเป็นมีการเพิ่มประเภทการอ้างอิงบางประเภท (สไลซ์แผนที่และช่อง)


ภาษาโปรแกรม Go: คำถามที่พบบ่อยเกี่ยวกับการออกแบบภาษา: เหตุใดจึงมีการอ้างอิงถึงแผนที่ชิ้นส่วนและช่องในขณะที่อาร์เรย์เป็นค่า

"มีประวัติมากมายเกี่ยวกับหัวข้อนั้นในช่วงต้นแผนที่และช่องสัญญาณเป็นตัวชี้ทางวากยสัมพันธ์และเป็นไปไม่ได้ที่จะประกาศหรือใช้อินสแตนซ์ที่ไม่ใช่ตัวชี้นอกจากนี้เราต้องดิ้นรนกับวิธีการทำงานของอาร์เรย์ในที่สุดเราก็ตัดสินใจว่าการแยกที่เข้มงวด ของพอยน์เตอร์และค่าต่างๆทำให้ภาษาใช้งานได้ยากขึ้นการแนะนำประเภทการอ้างอิงรวมถึงสไลซ์เพื่อจัดการกับรูปแบบการอ้างอิงของอาร์เรย์ช่วยแก้ปัญหาเหล่านี้ได้ประเภทการอ้างอิงเพิ่มความซับซ้อนที่น่าเสียใจให้กับภาษา แต่มีผลอย่างมากต่อการใช้งาน: Go กลายเป็น ภาษาที่มีประสิทธิผลและสะดวกสบายมากขึ้นเมื่อได้รับการแนะนำ "


การรวบรวมอย่างรวดเร็วเป็นเป้าหมายหลักในการออกแบบภาษาโปรแกรม Go ที่มีต้นทุน หนึ่งในผู้เสียชีวิตดูเหมือนจะเป็นความสามารถในการทำเครื่องหมายตัวแปร (ยกเว้นค่าคงที่เวลาคอมไพล์พื้นฐาน) และพารามิเตอร์ว่าไม่เปลี่ยนรูป ได้รับการร้องขอ แต่ปฏิเสธ


golang-nuts: go language. ข้อเสนอแนะและข้อสงสัยบางประการ

"การเพิ่ม const ให้กับระบบ type จะบังคับให้ปรากฏทุกที่และบังคับให้หนึ่งลบออกทุกที่หากมีการเปลี่ยนแปลงแม้ว่าอาจมีประโยชน์ในการทำเครื่องหมายวัตถุที่ไม่เปลี่ยนรูปในทางใดทางหนึ่ง แต่เราไม่คิดว่าคุณสมบัติประเภท const จะเป็นวิธี ไป."


FWIW "ประเภทอ้างอิง" ใน Go สามารถกำหนดใหม่ได้เช่นกัน พวกเขาเป็นเหมือนตัวชี้โดยนัยมากกว่า?
Matt Joiner

1
เป็นเพียงไวยากรณ์พิเศษสำหรับโครงสร้างที่มีตัวชี้ (และความยาวความจุ ... )
mk12

28

ไม่สามารถกำหนดการอ้างอิงใหม่ได้ในขณะที่พอยน์เตอร์สามารถ สิ่งนี้ทำให้พอยน์เตอร์มีประโยชน์ในหลาย ๆ สถานการณ์ที่ไม่สามารถใช้การอ้างอิงได้


19
การอ้างอิงที่กำหนดซ้ำได้นั้นเป็นปัญหาในการใช้งานเฉพาะภาษาหรือไม่
crantok
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.