2012-06-18 47 views
5

Trong ứng dụng của tôi, tôi thường xuyên chuyển các tham chiếu đến một chuỗi tĩnh. Tôi muốn tránh việc cấp phát bộ nhớ cho mỗi cuộc gọi, nhưng tôi đã không nhận được địa chỉ cho chuỗi ký tự của tôi.Tham chiếu đến các chuỗi ký tự trong Go

Tại sao không thể lấy địa chỉ của chuỗi ký tự (xem test1() trong ví dụ bên dưới)? Tôi đã hiểu sai cú pháp, hay nó là một hạn chế do các hoạt động bên trong của Go?

Nếu không thể, giải pháp nào là tốt nhất?

test2() hoạt động, nhưng nó sẽ phân bổ bộ nhớ cho var hej mỗi lần?
test3() sẽ không cấp phát bộ nhớ mới, nhưng tôi muốn tránh lộn xộn bên ngoài chức năng.

package main 

import "fmt" 

var konnichiwa = `こんにちは世界` 

// Gives the compile error `cannot take the address of "Hello world"` 
func test1() (*string) { return &`Hello world` } 

// Works fine 
func test2() (*string) { 
    hej := `Hej världen` 
    return &hej 
} 

func test3() (*string) { return &konnichiwa } 

func main(){ 
    fmt.Println(*test1()) 
    fmt.Println(*test2()) 
    fmt.Println(*test3()) 
} 

Cảm ơn bạn đã trợ giúp!

Trả lời

5

Lấy địa chỉ của một chữ (chuỗi, số, v.v.) là bất hợp pháp vì nó có ngữ nghĩa mơ hồ.

Bạn có đang sử dụng địa chỉ của hằng số thực tế không? Mà sẽ cho phép giá trị được sửa đổi (và có thể dẫn đến một lỗi thời gian chạy) hoặc bạn muốn phân bổ một đối tượng mới, sao chép các hằng số hơn và nhận được địa chỉ cho phiên bản mới?

Sự không rõ ràng này không tồn tại trong trường hợp test2 vì bạn đang xử lý một biến hiện có trong đó ngữ nghĩa được xác định rõ ràng. Điều tương tự sẽ không hoạt động nếu chuỗi được định nghĩa là const.

Đặc tả ngôn ngữ tránh sự mơ hồ này bằng cách rõ ràng không cho phép những gì bạn đang yêu cầu. Giải pháp là test2. Trong khi nó tiết kiệm hơn một chút, nó vẫn giữ các quy tắc đơn giản và rõ ràng.

Tất nhiên, mọi quy tắc có trường hợp ngoại lệ của nó, và tại Gò này liên quan đến literals composit: Sau đây là hợp pháp và được xác định như vậy trong spec:

func f() interface{} { 
    return &struct { 
     A int 
     B int 
    }{1, 2} 
} 
+0

Tôi có thể hiểu được điểm mơ hồ. Cảm ơn vì đã giải thích! – ANisus

6

Chuỗi đang chuyển là không thay đổi và chỉ là một con trỏ và chiều dài (tổng chiều dài: 2 từ).

Vì vậy, bạn không phải sử dụng con trỏ để xử lý hiệu quả.

Chỉ cần truyền chuỗi.

+0

Trong khi không phải là câu trả lời trực tiếp cho câu hỏi, đó là một giải pháp tốt cho vấn đề. +1 – ANisus

2
  1. Đi qua xung quanh một chuỗi không thường cấp phát bộ nhớ trong Go - đó là một loại giá trị (ptr tới byte và int len).

  2. Lấy một địa chỉ của một chữ chỉ được hỗ trợ cho literals composit như

    v := &T{1, "foo"}

    nhưng không cho các giá trị đơn giản như

    w := &1

    x := &"foo"

5

Đối với câu hỏi về giải pháp tốt nhất cho tình huống của bạn khi đi qua các chuỗi "tĩnh",

  1. Vượt qua loại chuỗi thay vì * chuỗi.
  2. Đừng đưa ra giả định về những gì đang diễn ra đằng sau hậu trường.

Thật hấp dẫn khi đưa ra lời khuyên "đừng lo lắng về việc phân bổ chuỗi" bởi vì điều đó thực sự đúng trong trường hợp bạn mô tả nơi cùng một chuỗi được truyền đi, có lẽ nhiều lần. Nó nói chung mặc dù, nó thực sự tốt để suy nghĩ về việc sử dụng bộ nhớ. Nó chỉ thực sự tồi tệ để đoán, và thậm chí còn tồi tệ hơn để đoán dựa trên kinh nghiệm với một ngôn ngữ khác.

Đây là phiên bản đã sửa đổi của chương trình của bạn. Nơi nào bạn đoán rằng bộ nhớ được phân bổ?

package main 

import "fmt" 

var konnichiwa = `こんにちは世界` 

func test1() *string { 
    s := `Hello world` 
    return &s 
} 

func test2() string { 
    return `Hej världen` 
} 

func test3() string { 
    return konnichiwa 
} 

func main() { 
    fmt.Println(*test1()) 
    fmt.Println(test2()) 
    fmt.Println(test3()) 
} 

Bây giờ yêu cầu trình biên dịch:

> go tool 6g -S t.go 

(tôi đặt tên cho chương trình t.go.) Tìm kiếm đầu ra cho các cuộc gọi đến runtime.new. Chỉ có một! Tôi sẽ làm hỏng nó cho bạn, đó là trong test1. Vì vậy, nếu không đi trên quá nhiều của một tiếp tuyến, cái nhìn nhỏ ở đầu ra trình biên dịch chỉ ra rằng chúng ta tránh phân bổ bằng cách làm việc với loại chuỗi chứ không phải là * chuỗi.

+2

Tôi không chỉ hiểu rõ hơn khi Go thực sự quyết định cấp phát bộ nhớ mới, nhưng thậm chí tốt hơn (hoặc tệ hơn!), Bạn cũng đã dạy tôi cách sử dụng 'go tool' để tự lắp ráp. Curses, bây giờ tôi sẽ ngồi phân tích mã lắp ráp của Go cho giờ vui vẻ. Cảm ơn! (Và có, tôi sẽ gắn bó với các giải pháp của bạn) – ANisus

+2

tại sao đây không phải là câu trả lời được chấp nhận? cảm ơn vì đã dạy chúng tôi cách câu cá :) –

Các vấn đề liên quan