2012-05-10 54 views
26

什麼是辦案最徹底的方法,如這樣的:Go的臨時地址?

func a() string { 
    /* doesn't matter */ 
} 

b *string = &a() 

這會產生錯誤:

cannot take the address of a()

我的理解是,Go會自動提升一個局部變量的堆,如果它的地址是拍攝。這裏清楚的是返回值的地址是要被採用的。處理這個問題的慣用方法是什麼?

+0

你想用相關建築來達到什麼目的? – Wolf

回答

23

地址運算符返回指向具有「家」的東西的指針,例如,一個變量。代碼中表達式的值是「無家可歸」。如果你真的需要一個*字符串,你必須這樣做,在2個步驟:

tmp := a(); b := &tmp 

注意的是,雖然有完全有效的用例*字符串,很多時候,它用起來是錯誤的。在Go string是一個值類型,但傳遞一個便宜的(一個指針和一個int)。字符串的是不可變的,因此更改*string更改「家」指向的位置,而不是字符串值,因此大多數情況下根本不需要*string

+1

或從'a()'返回一個'* string' – thwd

+2

你也可以取一個沒有「home」的複合文字的地址 – newacct

+0

複合文字不一定有家,但規格保證一個複合文字的地址是由'&'操作符擁有一個「家」(無論如何不可能有其他選擇,否則將不會有地址)。 – zzzz

0

a()不指向變量,因爲它在堆棧上。你不能指向堆棧(你爲什麼?)。

你可以做,如果你想

va := a() 
b := &va 

但是你真正想實現的是有些不清楚。

+0

不,現在我相信'va'會被存儲在堆上。 –

+0

有人可以確認在給出的示例代碼中,由於以下行上的地址操作符,'va'將被存儲在堆上? –

+0

真正的問題似乎是「如果聲明塊被垃圾回收但是b仍然存在,va的值是否會被GC清除?」。不是嗎?我認爲這個值不會因爲b中的指針而變得垃圾。 –

12

請參閱Go language spec的相關章節。

  1. 東西是尋址::&只能在使用變量,指針間接,切片索引操作,可尋址結構的字段選擇器,可尋址陣列的數組索引操作; OR
  2. 複合聲明

你有什麼是沒有的,所以這是行不通的。

即使您能做到,我甚至不確定這意味着什麼。取一個函數調用的結果的地址?通常,您將某個指針傳遞給某個人,因爲您希望他們能夠指定指向的對象,並查看原始變量中的更改。但函數調用的結果是暫時的;除非先將它分配給某個東西,否則沒有其他人「看到」它。

如果創建指針的目的是創建一個動態生命週期的東西,類似於new()或者採用複合文字的地址,那麼可以將函數調用的結果分配給一個變量,並將地址那。

+0

如果一個函數返回一個非指針值,那麼我認爲它假設返回值存儲在堆棧中。但是,如果您立即採用返回值的地址,我沒有發現任何促進存儲返回堆的模糊性,正常情況下?地獄,因爲調用者框架還沒有任何機會利用返回值,調用者可以很容易地在返回時直接將結果複製到堆中。 –

+0

+1規範參考 –

5

到底你提議去應該允許您採取任何表達的地址,例如:

i,j := 1,2 
var p *int = &(i+j) 
println(*p) 

目前圍棋編譯器打印錯誤:cannot take the address of i + j

在我看來,允許程序員採取任何表達式的地址:

  • 似乎不是非常有用(即:它似乎在實際中發生的可能性非常小去程序)。
  • 這會使編譯器和語言規範複雜化。

似乎適得其反,會使編譯器和規範複雜化,收益不大。

+0

嗯,這實際上是很有道理的。我忽略了函數調用是一個表達式的事實。 –

+0

錯誤消息似乎很清楚Go如何解釋&(i + j),但可能不是。如果程序員把它解釋爲「執行表達式(i + j)所產生的值的地址」,那麼這可能是合理的(也許不是在Go中),有些實際上是這樣解釋的。 (我對Go是對是錯沒有意見......但是'a'表達式?&爲它的作品,'&(a)'也可以:-)所以,Go是否反對錶達或一個堆棧分配的值(並簡單地通過名稱「i + j」來調用它)?有關係嗎? – hutch

+1

@hutch Go(至少在當前的實現中)反對錶達式。從程序員的角度來看,編譯器如何做並不重要。 – 2012-05-11 17:52:35

1

我最近被綁在關於類似的東西。

首先談論您的示例字符串是一種干擾,使用結構相反,它重新寫一樣的東西:

func a() MyStruct { 
    /* doesn't matter */ 
} 

var b *MyStruct = &a() 

這不能編譯,因爲你不能把一個地址()。所以這樣做:

func a() MyStruct { 
    /* doesn't matter */ 
} 

tmpA := a() 
var b *MyStruct = &tmpA 

這將編譯,但你已經返回堆棧上的MYSTRUCT,就堆到存儲MYSTRUCT分配足夠的空間,然後從堆棧堆複製的內容。如果你想避免這種情況,然後把它寫這樣的:

func a2() *MyStruct { 
    /* doesn't matter as long as MyStruct is created on the heap (e.g. use 'new') */ 
} 

var a *MyStruct = a2() 

複製通常是便宜的,但這些結構可能是很大的。更糟糕的是,當你想修改結構並讓它「粘住」時,你不能複製,然後修改副本。

無論如何,當您使用返回類型的接口{}時,它會變得更有趣。接口{}可以是結構或指向結構體的指針。出現相同的複製問題。