Có ích lợi gì khi có con trỏ trong cờ vây?


98

Tôi biết rằng các con trỏ trong Go cho phép đột biến các đối số của một hàm, nhưng sẽ không đơn giản hơn nếu chúng chỉ sử dụng các tham chiếu (với const thích hợp hoặc các định tính có thể thay đổi). Bây giờ chúng ta có các con trỏ và đối với một số loại tích hợp sẵn như bản đồ và kênh ngầm chuyển qua tham chiếu.

Tôi đang thiếu thứ gì đó hay các con trỏ trong cờ vây chỉ là một sự phức tạp không cần thiết?


1
Đây là một câu hỏi có thể giúp làm rõ: stackoverflow.com/questions/795160/… Có sự khác biệt giữa chuyển tham chiếu theo giá trị và chuyển thực sự theo tham chiếu.
R. Martinho Fernandes

1
Lưu ý: câu hỏi là về Java, nhưng nó cũng áp dụng ở đây.
R. Martinho Fernandes

1
"và đối với một số loại tích hợp như bản đồ và kênh ngầm chuyển qua tham chiếu." Không, mọi thứ đều là giá trị truyền trong cờ vây. Một số kiểu là (được mô tả không chính thức là) kiểu tham chiếu, vì chúng có trạng thái có thể thay đổi bên trong.
newacct

Vấn đề với câu hỏi này là "tham chiếu" không phải là một thứ duy nhất có các thuộc tính được xác định rõ. Thuật ngữ "tài liệu tham khảo" rất mơ hồ. Chúng ta có thể thấy trong câu trả lời có bao nhiêu người đọc những thứ khác nhau thành từ "tài liệu tham khảo". Vì vậy, câu hỏi này nên giải thích chính xác những khác biệt giữa con trỏ cờ vây và các tham chiếu mà câu hỏi có trong đầu.
mtraceur

Câu trả lời:


36

Tôi thực sự thích ví dụ được lấy từ 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
}

tương phản với

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

41
Câu hỏi là "tại sao chúng ta có con trỏ thay vì tham chiếu " và tôi không hiểu tại sao ví dụ này không hoạt động với tham chiếu.
AndreKR

@AndreKR Bởi vì chúng ta có thể chọn chuyển theo tham chiếu hoặc chuyển theo giá trị. Có một số trường hợp mà cả hai đều có thể được mong muốn.
JDSweetBeat

9
@DJMethaneMan Đó là "con trỏ so với tham chiếu", không phải "con trỏ so với giá trị truyền qua"!
AndreKR,

Như một nhận xét bên lề, tham chiếu chuyển qua đã được thêm vào C # 2.0 thông qua từ khóa "ref". Tất nhiên con trỏ vẫn thuận tiện hơn trong một số trường hợp nhất định, bởi vì chúng ta có thể có con trỏ tới con trỏ tới con trỏ ...
robbie fan

33

Con trỏ hữu ích vì một số lý do. Con trỏ cho phép kiểm soát việc bố trí bộ nhớ (ảnh hưởng đến hiệu quả của bộ đệm CPU). Trong Go, chúng ta có thể xác định một cấu trúc mà tất cả các thành viên nằm trong bộ nhớ liền kề:

type Point struct {
  x, y int
}

type LineSegment struct {
  source, destination Point
}

Trong trường hợp này, các Pointcấu trúc được nhúng trong LineSegmentcấu trúc. Nhưng không phải lúc nào bạn cũng có thể nhúng dữ liệu trực tiếp. Nếu bạn muốn hỗ trợ các cấu trúc như cây nhị phân hoặc danh sách liên kết, thì bạn cần hỗ trợ một số loại con trỏ.

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

Java, Python, v.v. không gặp vấn đề này vì nó không cho phép bạn nhúng các kiểu kết hợp, vì vậy không cần phải phân biệt cú pháp giữa nhúng và trỏ.

Các vấn đề với cấu trúc Swift / C # được giải quyết bằng con trỏ Go

Một lựa chọn tốt để thực hiện điều này cũng để phân biệt giữa structclassnhư C # và Swift không. Nhưng điều này có những hạn chế. Mặc dù bạn thường có thể chỉ định rằng một hàm nhận cấu trúc làm inouttham số để tránh sao chép cấu trúc, nhưng nó không cho phép bạn lưu trữ các tham chiếu (con trỏ) đến cấu trúc. Điều này có nghĩa là bạn không bao giờ có thể coi một cấu trúc là một kiểu tham chiếu khi bạn thấy rằng hữu ích, ví dụ: để tạo một bộ phân bổ nhóm (xem bên dưới).

Bộ phân bổ bộ nhớ tùy chỉnh

Sử dụng con trỏ, bạn cũng có thể tạo bộ phân bổ nhóm của riêng mình (điều này rất đơn giản với nhiều kiểm tra được loại bỏ để chỉ hiển thị nguyên tắc):

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
}

Hoán đổi hai giá trị

Con trỏ cũng cho phép bạn thực hiện swap. Đó là hoán đổi giá trị của hai biến:

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

Phần kết luận

Java chưa bao giờ có thể thay thế hoàn toàn C ++ cho lập trình hệ thống tại những nơi như Google, một phần vì hiệu suất không thể được điều chỉnh đến cùng mức mở rộng do thiếu khả năng kiểm soát việc bố trí và sử dụng bộ nhớ (bộ nhớ cache ảnh hưởng đáng kể đến hiệu suất). Go đã nhằm mục đích thay thế C ++ trong nhiều lĩnh vực và do đó cần hỗ trợ con trỏ.


7
C # cho phép chuyển cấu trúc bằng tham chiếu. Xem từ khóa "ref" và "out".
olegz

1
Được rồi, nó giống như Swift. Tôi sẽ nghĩ về một cách để cập nhật ví dụ của tôi.
Erik Engheim

29

Các tham chiếu không thể được chỉ định lại, trong khi con trỏ có thể. Điều này làm cho con trỏ hữu ích trong nhiều trường hợp không thể sử dụng tham chiếu.


17
Liệu các tham chiếu có thể chuyển nhượng lại hay không là một vấn đề triển khai theo ngôn ngữ cụ thể.
crantok

28

Cờ vây được thiết kế để trở thành một ngôn ngữ ngắn gọn, tối giản. Do đó, nó bắt đầu chỉ với các giá trị và con trỏ. Sau đó, do cần thiết, một số kiểu tham chiếu (lát cắt, bản đồ và kênh) đã được thêm vào.


Ngôn ngữ lập trình Go: Câu hỏi thường gặp về thiết kế ngôn ngữ: Tại sao bản đồ, lát cắt và kênh là tham chiếu trong khi mảng là giá trị?

"Có rất nhiều lịch sử về chủ đề đó. Ban đầu, bản đồ và kênh là con trỏ cú pháp và không thể khai báo hoặc sử dụng một phiên bản không phải là con trỏ. Ngoài ra, chúng tôi đã vật lộn với cách hoạt động của các mảng. Cuối cùng, chúng tôi quyết định rằng sự tách biệt chặt chẽ của con trỏ và giá trị khiến ngôn ngữ khó sử dụng hơn. Việc giới thiệu các kiểu tham chiếu, bao gồm các lát cắt để xử lý dạng tham chiếu của mảng, đã giải quyết những vấn đề này. Các kiểu tham chiếu thêm một số phức tạp đáng tiếc cho ngôn ngữ nhưng chúng có ảnh hưởng lớn đến khả năng sử dụng: Go trở thành ngôn ngữ hiệu quả hơn, thoải mái hơn khi chúng được giới thiệu. "


Biên dịch nhanh là mục tiêu thiết kế chính của ngôn ngữ lập trình Go; điều đó có chi phí của nó. Một trong những thương vong xuất hiện là khả năng đánh dấu các biến (ngoại trừ các hằng số thời gian biên dịch cơ bản) và các tham số là bất biến. Nó đã được yêu cầu, nhưng bị từ chối.


golang-nut: đi ngôn ngữ. Một số phản hồi và nghi ngờ.

"Việc thêm const vào hệ thống kiểu buộc nó xuất hiện ở mọi nơi và buộc người ta phải xóa nó ở mọi nơi nếu có điều gì đó thay đổi. đi."


FWIW, "loại tham chiếu" trong Go cũng có thể chuyển nhượng lại. Chúng giống như những con trỏ ngầm hơn?
Matt Joiner

1
Chúng chỉ là cú pháp đặc biệt cho các cấu trúc chứa một con trỏ (và độ dài, dung lượng, ...).
mk12,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.