Làm cách nào để thực hiện ký tự * int64 trong Go?


103

Tôi có một kiểu cấu trúc với một *int64trường.

type SomeType struct {
    SomeField *int64
}

Tại một số thời điểm trong mã của tôi, tôi muốn khai báo một nghĩa đen của điều này (giả sử, khi tôi biết giá trị đã nói phải là 0 hoặc trỏ đến 0, bạn biết tôi muốn nói gì)

instance := SomeType{
    SomeField: &0,
}

... ngoại trừ điều này không hoạt động

./main.go:xx: cannot use &0 (type *int) as type *int64 in field value

Vì vậy, tôi thử cái này

instance := SomeType{
    SomeField: &int64(0),
}

... nhưng điều này cũng không hoạt động

./main.go:xx: cannot take the address of int64(0)

Làm thế nào để tôi làm điều này? Giải pháp duy nhất tôi có thể đưa ra là sử dụng biến giữ chỗ

var placeholder int64
placeholder = 0

instance := SomeType{
    SomeField: &placeholder,
}

Lưu ý: &0cú pháp hoạt động tốt khi nó là * int thay vì an *int64. Chỉnh sửa: không nó không. Xin lỗi về điều này.

Biên tập:

Rõ ràng có quá nhiều mơ hồ cho câu hỏi của tôi. Tôi đang tìm kiếm một cách để theo nghĩa đen nhà nước một *int64. Điều này có thể được sử dụng bên trong một hàm tạo, hoặc để chỉ ra các giá trị cấu trúc theo nghĩa đen, hoặc thậm chí làm đối số cho các hàm khác. Nhưng các chức năng trợ giúp hoặc sử dụng một loại khác không phải là giải pháp tôi đang tìm kiếm.


1
Con trỏ đến int không may là int chiếm cùng một lượng không gian như một con trỏ, vì vậy bạn không tiết kiệm được dung lượng. Nó chỉ thêm một giá trị NULL thường phức tạp hơn giá trị của nó. Trong hầu hết các trường hợp, số 0 sẽ ổn. Nếu bạn cần một giá trị bổ sung, bool "IsValidSomeField" cũng hoạt động và nếu bạn đặt tên cho bool đó tốt hơn, nó có thể nói rõ hơn về lý do tại sao bạn cần giá trị bổ sung đó, điều này rất tốt cho việc đọc.
voutasaurus

2
Bạn có thể sử dụng con trỏ gói , ví dụ:var _ *int64 = pointer.Int64(64)
Xor

Câu trả lời:


212

Đặc tả Ngôn ngữ Go ( Toán tử địa chỉ ) không cho phép lấy địa chỉ của một hằng số (không phải của một hằng số chưa được định kiểu cũng như không phải của một hằng số được định kiểu ).

Toán hạng phải có thể định địa chỉ được , nghĩa là một biến, hướng con trỏ hoặc thao tác lập chỉ mục lát cắt; hoặc một bộ chọn trường của toán hạng cấu trúc có thể định địa chỉ; hoặc hoạt động lập chỉ mục mảng của một mảng có thể định địa chỉ. Là một ngoại lệ đối với yêu cầu về khả năng giải quyết, x[trong biểu thức của &x] cũng có thể là một chữ tổng hợp (có thể được đặt trong ngoặc đơn) .

Để biết lý do tại sao điều này không được phép, hãy xem câu hỏi liên quan: Tìm địa chỉ của hằng số khi di chuyển . Một câu hỏi tương tự (tương tự không được phép lấy địa chỉ của nó): Làm cách nào để lưu trữ tham chiếu đến kết quả của một thao tác trong Go?

Tùy chọn của bạn (thử tất cả trên Go Playground ):

1) Với new()

Bạn chỉ cần sử dụng new()hàm nội trang để cấp phát một giá trị 0 mới int64và lấy địa chỉ của nó:

instance := SomeType{
    SomeField: new(int64),
}

Nhưng lưu ý rằng điều này chỉ có thể được sử dụng để cấp phát và lấy một con trỏ đến giá trị 0 của bất kỳ kiểu nào.

2) Với biến trợ giúp

Đơn giản nhất và được khuyến nghị cho các phần tử khác 0 là sử dụng biến trợ giúp có địa chỉ có thể được sử dụng:

helper := int64(2)
instance2 := SomeType{
    SomeField: &helper,
}

3) Với chức năng trợ giúp

Lưu ý: Các hàm trợ giúp để thu được một con trỏ đến giá trị khác 0 có sẵn trong github.com/icza/goxthư viện của tôi , trong goxgói, vì vậy bạn không cần phải thêm chúng vào tất cả các dự án của mình khi bạn cần.

Hoặc nếu bạn cần điều này nhiều lần, bạn có thể tạo một hàm trợ giúp phân bổ và trả về *int64:

func create(x int64) *int64 {
    return &x
}

Và sử dụng nó:

instance3 := SomeType{
    SomeField: create(3),
}

Lưu ý rằng chúng tôi thực sự không cấp phát bất cứ thứ gì, trình biên dịch Go đã làm điều đó khi chúng tôi trả về địa chỉ của đối số hàm. Trình biên dịch Go thực hiện phân tích thoát và phân bổ các biến cục bộ trên heap (thay vì ngăn xếp) nếu chúng có thể thoát khỏi hàm. Để biết chi tiết, hãy xem Trả về một phần của mảng cục bộ trong hàm Go có an toàn không?

4) Với chức năng ẩn danh một lớp

instance4 := SomeType{
    SomeField: func() *int64 { i := int64(4); return &i }(),
}

Hoặc như một giải pháp thay thế (ngắn hơn):

instance4 := SomeType{
    SomeField: func(i int64) *int64 { return &i }(4),
}

5) Với phần chữ, lập chỉ mục và lấy địa chỉ

Nếu bạn muốn *SomeFieldkhác hơn 0, thì bạn cần một cái gì đó có thể giải quyết.

Bạn vẫn có thể làm điều đó, nhưng điều đó thật tệ:

instance5 := SomeType{
    SomeField: &[]int64{5}[0],
}
fmt.Println(*instance2.SomeField) // Prints 5

Điều xảy ra ở đây là một []int64lát cắt được tạo bằng một chữ, có một phần tử ( 5). Và nó được lập chỉ mục (phần tử thứ 0) và địa chỉ của phần tử thứ 0 được lấy. Trong nền, một mảng [1]int64cũng sẽ được cấp phát và sử dụng làm mảng hỗ trợ cho lát cắt. Vì vậy, có rất nhiều bảng điều khiển ở đây.

6) Với một chữ cấu trúc trợ giúp

Hãy xem xét ngoại lệ đối với các yêu cầu về khả năng giải quyết:

Là một ngoại lệ đối với yêu cầu về khả năng giải quyết, x[trong biểu thức của &x] cũng có thể là một chữ tổng hợp (có thể được đặt trong ngoặc đơn) .

Điều này có nghĩa là lấy địa chỉ của một ký tự tổng hợp, ví dụ như một ký tự cấu trúc là ok. Nếu chúng ta làm như vậy, chúng ta sẽ có giá trị struct được phân bổ và một con trỏ thu được đến nó. Nhưng nếu vậy, một yêu cầu khác sẽ có sẵn cho chúng tôi: "bộ chọn trường của toán hạng cấu trúc có thể địa chỉ" . Vì vậy, nếu ký tự struct chứa một trường kiểu int64, chúng ta cũng có thể lấy địa chỉ của trường đó!

Hãy xem tùy chọn này hoạt động. Chúng tôi sẽ sử dụng loại cấu trúc trình bao bọc này:

type intwrapper struct {
    x int64
}

Và bây giờ chúng ta có thể làm:

instance6 := SomeType{
    SomeField: &(&intwrapper{6}).x,
}

Lưu ý rằng điều này

&(&intwrapper{6}).x

nghĩa là như sau:

& ( (&intwrapper{6}).x )

Nhưng chúng ta có thể bỏ qua dấu ngoặc đơn "bên ngoài" vì toán tử địa chỉ &được áp dụng cho kết quả của biểu thức bộ chọn .

Cũng lưu ý rằng trong nền, điều sau sẽ xảy ra (đây cũng là một cú pháp hợp lệ):

&(*(&intwrapper{6})).x

7) Với nghĩa đen cấu trúc ẩn danh trợ giúp

Nguyên tắc giống như trường hợp số 6, nhưng chúng ta cũng có thể sử dụng một nghĩa đen struct ẩn danh, vì vậy không cần định nghĩa kiểu cấu trúc helper / wrapper:

instance7 := SomeType{
    SomeField: &(&struct{ x int64 }{7}).x,
}

1
điều này khác về mặt chức năng so với ví dụ cuối cùng trong câu hỏi với trình giữ chỗ, vì hai instanceđối tượng khác nhau sẽ trỏ đến hai int64s khác nhau . Nhưng nó dường như Ops đáp ứng ý định đầy đủ
Ryan Haining

2
điều này chắc chắn trả lời câu hỏi của tôi. Nhưng nó khiến tôi khá khó chịu :,&[]int64{2}[0] Tôi cảm thấy như dựa trên mô tả về hằng số tại blog.golang.org/constants, điều này sẽ hoạt động như vậy &0.
ThisGuy

@RyanHaining Và điều gì sẽ xảy ra nếu nó hoạt động giống như cùng một địa chỉ sẽ được chỉ định? Nếu hai instanceđối tượng khác nhau trỏ đến giống nhau int64và một đối tượng sẽ sửa đổi đối tượng trỏ, cả hai sẽ thay đổi. Và điều gì sẽ xảy ra nếu bây giờ bạn sử dụng cùng một chữ để tạo chữ thứ 3 instance? Cùng một địa chỉ bây giờ sẽ trỏ đến một int64giá trị khác ... vì vậy một địa chỉ khác nên được sử dụng, nhưng tại sao lại sử dụng cùng một địa chỉ trong trường hợp của 2 đầu tiên?
icza

@icza, bạn thường sẽ không muốn họ trỏ đến cùng một đối tượng, tôi không nói là bạn sẽ làm như vậy. Tôi chỉ chỉ ra sự khác biệt.
Ryan Haining

4
@Conslo Hằng số được đánh giá tại thời điểm biên dịch. Giá trị con trỏ hợp lệ, địa chỉ bộ nhớ hợp lệ chỉ tồn tại trong thời gian chạy, vì vậy điều này không giống với hằng số.
icza

6

Sử dụng một hàm trả về địa chỉ của một biến int64 để giải quyết vấn đề.

Trong đoạn mã dưới đây, chúng tôi sử dụng hàm fchấp nhận một số nguyên và trả về một giá trị con trỏ chứa địa chỉ của số nguyên. Bằng cách sử dụng phương pháp này, chúng ta có thể dễ dàng giải quyết vấn đề trên.

type myStr struct {
    url *int64
}

func main() {
    f := func(s int64) *int64 {
        return &s
    }
    myStr{
        url: f(12345),
    }
}
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.