Làm cách nào để in giá trị con trỏ của một đối tượng Go? Giá trị con trỏ có nghĩa là gì?


83

Tôi chỉ đang chơi với cờ vây và chưa có mô hình tinh thần tốt về thời điểm cấu trúc được chuyển theo giá trị hoặc bằng tham chiếu.

Đây có thể là một câu hỏi rất ngớ ngẩn nhưng tôi chỉ muốn thử nghiệm một chút và xem liệu tôi vẫn đang làm việc trên cùng một đối tượng hay tôi đã tạo một bản sao của nó (chuyển nó theo giá trị).

Có cách nào để in con trỏ (hoặc id nội bộ nếu giá trị con trỏ được thay đổi bởi gc) của một đối tượng không?

package main

import ( "runtime" )

type Something struct {
    number int
    queue chan int
}

func gotest( s *Something, done chan bool ) {
    println( "from gotest:")
    println( &s )
    for num := range s.queue {
        println( num )
        s.number = num
    }
    done <- true
}

func main() {
    runtime.GOMAXPROCS(4)
    s := new(Something)
    println(&s)
    s.queue = make(chan int)
    done := make(chan bool)
    go gotest(s, done)
    s.queue <- 42
    close(s.queue)
    <- done
    println(&s)
    println(s.number)
}

cung cấp cho các cửa sổ của tôi (phiên bản biên dịch 8g):

0x4930d4
from gotest:
0x4974d8
42
0x4930d4
42

Tại sao giá trị con trỏ trong quy trình go lại hiển thị một giá trị khác? Số lượng trên vật thể ban đầu đã thay đổi nên nó đang hoạt động với cùng một vật. Có cách nào để xem id đối tượng liên tục không?

Câu trả lời:


114

Các đối số của hàm Go được truyền theo giá trị.

Đầu tiên, hãy loại bỏ các phần không liên quan trong ví dụ của bạn, để chúng tôi có thể dễ dàng nhận thấy rằng bạn chỉ đang chuyển một đối số theo giá trị. Ví dụ,

package main

import "fmt"

func byval(q *int) {
    fmt.Printf("3. byval -- q %T: &q=%p q=&i=%p  *q=i=%v\n", q, &q, q, *q)
    *q = 4143
    fmt.Printf("4. byval -- q %T: &q=%p q=&i=%p  *q=i=%v\n", q, &q, q, *q)
    q = nil
}

func main() {
    i := int(42)
    fmt.Printf("1. main  -- i  %T: &i=%p i=%v\n", i, &i, i)
    p := &i
    fmt.Printf("2. main  -- p %T: &p=%p p=&i=%p  *p=i=%v\n", p, &p, p, *p)
    byval(p)
    fmt.Printf("5. main  -- p %T: &p=%p p=&i=%p  *p=i=%v\n", p, &p, p, *p)
    fmt.Printf("6. main  -- i  %T: &i=%p i=%v\n", i, &i, i)
}

Đầu ra:

1. main  -- i  int: &i=0xf840000040 i=42
2. main  -- p *int: &p=0xf8400000f0 p=&i=0xf840000040  *p=i=42
3. byval -- q *int: &q=0xf8400000d8 q=&i=0xf840000040  *q=i=42
4. byval -- q *int: &q=0xf8400000d8 q=&i=0xf840000040  *q=i=4143
5. main  -- p *int: &p=0xf8400000f0 p=&i=0xf840000040  *p=i=4143
6. main  -- i  int: &i=0xf840000040 i=4143

Trong hàm main, ilà một intbiến tại vị trí bộ nhớ ( &i) 0xf800000040với giá trị ban đầu ( i) 42.

Trong hàm main, plà một con trỏ đến một intbiến tại vị trí bộ nhớ ( &p) 0xf8000000f0với giá trị ( p= &i) 0xf800000040trỏ đến intgiá trị ( *p= i) 42.

Trong hàm main, byval(p)là một lệnh gọi hàm gán giá trị ( p= &i) 0xf800000040của đối số tại vị trí bộ nhớ ( &p) 0xf8000000f0cho byvaltham số hàm qtại vị trí bộ nhớ ( &q) 0xf8000000d8. Nói cách khác, bộ nhớ được cấp phát cho byvaltham số qvà giá trị của main byvalđối số pđược gán cho nó; các giá trị của pqban đầu giống nhau, nhưng các biến pqkhác nhau.

Trong hàm byval, sử dụng pointer q( *int), là bản sao của pointer p( *int), integer *q( i) được đặt thành giá trị int mới 4143. Vào cuối trước khi trở lại. con trỏ qđược đặt thành nil(giá trị 0), không ảnh hưởng đến pqlà một bản sao.

Trong hàm main, plà một con trỏ đến một intbiến tại vị trí bộ nhớ ( &p) 0xf8000000f0với giá trị ( p= &i) 0xf800000040trỏ đến intgiá trị mới ( *p= i) 4143.

Trong hàm main, ilà một intbiến tại vị trí bộ nhớ ( &i) 0xf800000040với giá trị cuối cùng ( i) 4143.

Trong ví dụ của bạn, mainbiến hàm sđược sử dụng làm đối số cho lệnh gotestgọi hàm không giống với gotesttham số hàm s. Chúng có cùng tên, nhưng là các biến khác nhau với các phạm vi và vị trí bộ nhớ khác nhau. Tham số hàm sẩn đối số gọi hàm s. Đó là lý do tại sao trong ví dụ của tôi, tôi đặt tên cho đối số và các biến tham số pqtương ứng để nhấn mạnh sự khác biệt.

Trong ví dụ của bạn, ( &s) 0x4930d4là địa chỉ của vị trí bộ nhớ cho biến strong hàm mainđược sử dụng làm đối số cho lệnh gọi hàm gotest(s, done)0x4974d8là địa chỉ của vị trí bộ nhớ cho gotesttham số hàm s. Nếu bạn đặt tham số s = nilở cuối hàm gotest, nó không ảnh hưởng đến biến strong main; strong mainstrong gotestlà các vị trí bộ nhớ riêng biệt. Xét về các loại, &sis **Something, sis *Something, and *sis Something. &slà một con trỏ tới (địa chỉ của vị trí bộ nhớ) s, là một con trỏ tới (địa chỉ của vị trí bộ nhớ) một biến ẩn danh kiểuSomething. Xét về giá trị, main.&s != gotest.&s, main.s == gotest.s, main.*s == gotest.*s, và main.s.number == gotest.s.number.

Bạn nên nghe theo lời khuyên của mkb's sage và ngừng sử dụng println(&s). fmtVí dụ: sử dụng gói,

fmt.Printf("%v %p %v\n", &s, s, *s)

Các con trỏ có cùng giá trị khi chúng trỏ đến cùng một vị trí bộ nhớ; con trỏ có các giá trị khác nhau khi chúng trỏ đến các vị trí bộ nhớ khác nhau.


Trong ví dụ của tôi, gotest nhận một con trỏ đến 'Something', vì vậy tôi giả sử nó đang tham chiếu đến cùng một đối tượng và rõ ràng là sau khi tôi thay đổi giá trị bên trong go-routine thì đối tượng cũng bị thay đổi giá trị trong hàm main . Giá trị con trỏ được in là khác nhau.
Jeroen Dirks

@JamesDean Trong ví dụ của bạn, bạn đang in giá trị con trỏ & loại s ** Cái gì đó, không giống với loại giá trị con trỏ * Cái gì đó. Tôi đã sửa lại ví dụ của mình để chuyển một con trỏ theo giá trị.
peterSO

@James Dean Bạn đã in ra địa chỉ của con trỏ (tức là một con trỏ tới scon trỏ), - các con trỏ được chuyển theo giá trị, địa chỉ của skhông giống như s. Nếu hàm gotest của bạn là println( s )intead, nó sẽ in giá trị con trỏ của anh ta.
nos

Oh bây giờ tôi thấy những gì đang xảy ra. Bằng cách thực hiện println (& s), tôi đã in địa chỉ của con trỏ thay vì giá trị con trỏ. Nếu tôi đã thực hiện println (các) nó sẽ hiển thị cùng một con trỏ trong hàm main và hàm go.
Jeroen Dirks

@JamesDean Chính xác. Trong Go cũng như trong C, đối với s * Something, điều cần thiết là phải biết sự khác biệt giữa & s, s và * s.
peterSO

6

Trong cờ vây, các đối số được truyền theo giá trị.

package main

import "fmt"

type SomeStruct struct {
    e int
}

// struct passed by value
func v(v SomeStruct) {
    fmt.Printf("v: %p %v\n", &v, v)
    v.e = 2
    fmt.Printf("v: %p %v\n", &v, v)
}

// pointer to struct passed by value
func p(p *SomeStruct) {
    fmt.Printf("p: %p %v\n", p, *p)
    p.e = 2
    fmt.Printf("p: %p %v\n", p, *p)
}

func main() {
    var s SomeStruct
    s.e = 1
    fmt.Printf("s: %p %v\n", &s, s)
    v(s)
    fmt.Printf("s: %p %v\n", &s, s)
    p(&s)
    fmt.Printf("s: %p %v\n", &s, s)
}

Đầu ra:

s: 0xf800000040 {1}
v: 0xf8000000e0 {1}
v: 0xf8000000e0 {2}
s: 0xf800000040 {1}
p: 0xf800000040 {1}
p: 0xf800000040 {2}
s: 0xf800000040 {2}

2
type sometype struct { }
a := sometype {}
b := int(2)
println("Ptr to a", &a)
println("Ptr to b", &b)

4
Bạn không nên sử dụng println nội trang mà hãy sử dụng thứ gì đó thích hợp từ gói fmt: golang.org/doc/go_spec.html#Bootstrapping
mkb

2

Làm cách nào để in giá trị con trỏ của một đối tượng Go?

package main

import (
    "fmt"
)

func main() {
    a := 42
    fmt.Println(&a)
}

kết quả trong:

0x1040a124

Giá trị con trỏ có nghĩa là gì?

Theo Wikipedia :

Một con trỏ tham chiếu đến một vị trí trong bộ nhớ


1
package main

import "fmt"

func zeroval(ival int) {
     ival = 0
}

func zeroptr(iptr *int) {
     *iptr = 0
}

func main() {
    i := 1
    fmt.Println("initial:", i)
    zeroval(i)
    fmt.Println("zeroval:", i)
    //The &i syntax gives the memory address of i, i.e. a pointer to i.
    zeroptr(&i)
    fmt.Println("zeroptr:", i)
    //Pointers can be printed too.
    fmt.Println("pointer:", &i)
}

ĐẦU RA:

$ go run pointers.go
initial: 1
zeroval: 1
zeroptr: 0
pointer: 0x42131100
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.