2012-06-02 18 views
96

मैं जाने के लिए नया हूं और मुझे सी-स्टाइल स्टैक-आधारित प्रोग्रामिंग के बीच थोड़ा सा असंगत विसंगति का सामना करना पड़ रहा है जहां स्वचालित चर ढेर पर रहते हैं और आवंटित स्मृति ढेर और पायथन शैली के ढेर-आधारित प्रोग्रामिंग पर रहता है जहां ढेर पर रहने वाली एकमात्र चीज ढेर पर वस्तुओं के संदर्भ/पॉइंटर्स हैं।गो में गोले के ढेर बनाम ढेर, और वे कैसे कचरा संग्रह से संबंधित हैं

func myFunction() (*MyStructType, error) { 
    var chunk *MyStructType = new(HeaderChunk) 

    ... 

    return chunk, nil 
} 


func myFunction() (*MyStructType, error) { 
    var chunk MyStructType 

    ... 

    return &chunk, nil 
} 

अर्थात एक नया struct आवंटित और इसे वापस:

जहां तक ​​मेरा बता सकते हैं, दो निम्नलिखित कार्य एक ही आउटपुट दे।

अगर मैं इसे सी में लिखा होता, तो पहले व्यक्ति ने ढेर पर एक वस्तु डाली होगी और दूसरा इसे ढेर पर रखेगा। पहला ढेर में एक सूचक वापस लौटाएगा, दूसरा स्टैक पर एक सूचक लौटाएगा, जो समारोह वापस लौटाए जाने तक वाष्पित हो जाता था, जो एक बुरी चीज होगी।

अगर मैं इसे पायथन (या सी # को छोड़कर कई अन्य आधुनिक भाषाओं) में लिखा था तो उदाहरण 2 संभव नहीं होता।

मुझे लगता है कि गो कचरा दोनों मूल्यों को एकत्र करता है, इसलिए उपर्युक्त दोनों रूप ठीक हैं।

के शब्दों में:

ध्यान दें कि, के विपरीत सी में, यह एक स्थानीय चर का पता वापस जाने के लिए पूरी तरह से ठीक है; फंक्शन रिटर्न के बाद चर के साथ जुड़े भंडारण जीवित रहता है। वास्तव में, एक समग्र शाब्दिक का पता लेते समय प्रत्येक बार मूल्यांकन किए जाने पर एक ताजा उदाहरण आवंटित करता है, इसलिए हम इन अंतिम दो पंक्तियों को जोड़ सकते हैं।

http://golang.org/doc/effective_go.html#functions

लेकिन यह सवाल के एक जोड़े को जन्म देती है।

1 - उदाहरण 1 में, संरचना ढेर पर घोषित की जाती है। उदाहरण 2 के बारे में क्या? क्या यह स्टैक पर उसी तरह घोषित किया गया है जैसे सी में होगा या यह ढेर पर भी जाता है?

2 - यदि उदाहरण 2 स्टैक पर घोषित किया गया है, तो फ़ंक्शन रिटर्न के बाद यह कैसे उपलब्ध रहता है?

3 - यदि उदाहरण 2 वास्तव में ढेर पर घोषित किया गया है, तो यह कैसे है कि संदर्भ के आधार पर structs मूल्य से पारित किया जाता है? इस मामले में पॉइंटर्स का क्या मतलब है?

उत्तर

111

यह ध्यान देने योग्य है कि शब्द "ढेर" और "ढेर" भाषा की कल्पना में कहीं भी दिखाई नहीं देते हैं। आपके प्रश्न को "... स्टैक पर घोषित किया गया है," और "... ढेर पर घोषित किया गया है," लेकिन ध्यान दें कि जाओ घोषणा वाक्यविन्यास स्टैक या ढेर के बारे में कुछ भी नहीं कहता है।

तकनीकी रूप से आपके सभी प्रश्नों के कार्यान्वयन पर निर्भर करता है। निश्चित रूप से, एक ढेर (प्रति goroutine!) और एक ढेर है और कुछ चीजें ढेर पर और कुछ ढेर पर जाते हैं। कुछ मामलों में संकलक कठोर नियमों का पालन करता है (जैसे "new हमेशा ढेर पर आवंटित करता है") और दूसरों में संकलक यह तय करने के लिए "विश्लेषण से बचता है" कि कोई वस्तु ढेर पर रह सकती है या यदि इसे ढेर पर आवंटित किया जाना चाहिए।

आपके उदाहरण 2 में, भागने का विश्लेषण सूचक से बचने के लिए पॉइंटर दिखाएगा और इसलिए संकलक को संरचना आवंटित करनी होगी। मुझे लगता है कि गो के वर्तमान कार्यान्वयन इस मामले में एक कठोर नियम का पालन करते हैं, हालांकि यह है कि अगर किसी संरचना के पते को संबोधित किया जाता है, तो संरचना ढेर पर जाती है।

प्रश्न 3 के लिए, हम शब्दावली के बारे में भ्रमित होने का जोखिम उठाते हैं। गो में सबकुछ मूल्य से गुजरता है, संदर्भ द्वारा कोई पास नहीं होता है। यहां आप एक सूचक मूल्य लौट रहे हैं। पॉइंटर्स का क्या मतलब है? अपने उदाहरण के निम्नलिखित संशोधन पर विचार करें:

type MyStructType struct{} 

func myFunction1() (*MyStructType, error) { 
    var chunk *MyStructType = new(MyStructType) 
    // ... 
    return chunk, nil 
} 

func myFunction2() (MyStructType, error) { 
    var chunk MyStructType 
    // ... 
    return chunk, nil 
} 

type bigStruct struct { 
    lots [1e6]float64 
} 

func myFunction3() (bigStruct, error) { 
    var chunk bigStruct 
    // ... 
    return chunk, nil 
} 

मैं myFunction2 संशोधित struct के पते के बजाय struct वापस जाने के लिए। myFunction1 और अब myFunction2 के विधानसभा उत्पादन की तुलना करें,

--- prog list "myFunction1" --- 
0000 (s.go:5) TEXT myFunction1+0(SB),$16-24 
0001 (s.go:6) MOVQ $type."".MyStructType+0(SB),(SP) 
0002 (s.go:6) CALL ,runtime.new+0(SB) 
0003 (s.go:6) MOVQ 8(SP),AX 
0004 (s.go:8) MOVQ AX,.noname+0(FP) 
0005 (s.go:8) MOVQ $0,.noname+8(FP) 
0006 (s.go:8) MOVQ $0,.noname+16(FP) 
0007 (s.go:8) RET  , 

--- prog list "myFunction2" --- 
0008 (s.go:11) TEXT myFunction2+0(SB),$0-16 
0009 (s.go:12) LEAQ chunk+0(SP),DI 
0010 (s.go:12) MOVQ $0,AX 
0011 (s.go:14) LEAQ .noname+0(FP),BX 
0012 (s.go:14) LEAQ chunk+0(SP),BX 
0013 (s.go:14) MOVQ $0,.noname+0(FP) 
0014 (s.go:14) MOVQ $0,.noname+8(FP) 
0015 (s.go:14) RET  , 

चिंता मत करो कि myFunction1 उत्पादन यहाँ peterSO की (उत्कृष्ट) जवाब में से अलग है। हम स्पष्ट रूप से विभिन्न कंपाइलर चला रहे हैं। अन्यथा, देखें कि मैंने myStructType की बजाय myStructType को वापस करने के लिए myFunction2 को संशोधित किया है। Runtime.new पर कॉल चला गया है, जो कुछ मामलों में एक अच्छी बात होगी। पर हालांकि

--- prog list "myFunction3" --- 
0016 (s.go:21) TEXT myFunction3+0(SB),$8000000-8000016 
0017 (s.go:22) LEAQ chunk+-8000000(SP),DI 
0018 (s.go:22) MOVQ $0,AX 
0019 (s.go:22) MOVQ $1000000,CX 
0020 (s.go:22) REP  , 
0021 (s.go:22) STOSQ , 
0022 (s.go:24) LEAQ chunk+-8000000(SP),SI 
0023 (s.go:24) LEAQ .noname+0(FP),DI 
0024 (s.go:24) MOVQ $1000000,CX 
0025 (s.go:24) REP  , 
0026 (s.go:24) MOVSQ , 
0027 (s.go:24) MOVQ $0,.noname+8000000(FP) 
0028 (s.go:24) MOVQ $0,.noname+8000008(FP) 
0029 (s.go:24) RET  , 

फिर भी कोई कॉल runtime.new को पकड़ो, यहाँ myFunction3 है, और हाँ यह वास्तव में मूल्य से एक 8MB वस्तु वापस जाने के लिए काम करता है। यह काम करता है, लेकिन आप आमतौर पर नहीं करना चाहते हैं। यहां एक पॉइंटर का बिंदु लगभग 8 एमबी ऑब्जेक्ट्स को धक्का देने से बचाना होगा।

+7

उत्कृष्ट धन्यवाद। मैं वास्तव में नहीं पूछ रहा था कि "पॉइंटर्स का बिंदु क्या है", यह "पॉइंटर्स की तरह व्यवहार करने के लिए पॉइंटर्स का बिंदु क्या है" की तरह अधिक था, और यह मामला वैसे भी आपके उत्तर द्वारा प्रस्तुत किया जाता है। – Joe

+7

असेंबली की एक छोटी व्याख्या की सराहना की जाएगी। – ElefEnt

39
type MyStructType struct{} 

func myFunction1() (*MyStructType, error) { 
    var chunk *MyStructType = new(MyStructType) 
    // ... 
    return chunk, nil 
} 

func myFunction2() (*MyStructType, error) { 
    var chunk MyStructType 
    // ... 
    return &chunk, nil 
} 

दोनों ही मामलों में, गो की वर्तमान कार्यान्वयन एक ढेर पर प्रकार MyStructType के struct के लिए स्मृति को आबंटित है और उसका पता लौट आते हैं। कार्य समकक्ष हैं; कंपाइलर एएसएम स्रोत वही है।

--- prog list "myFunction1" --- 
0000 (temp.go:9) TEXT myFunction1+0(SB),$8-12 
0001 (temp.go:10) MOVL $type."".MyStructType+0(SB),(SP) 
0002 (temp.go:10) CALL ,runtime.new+0(SB) 
0003 (temp.go:10) MOVL 4(SP),BX 
0004 (temp.go:12) MOVL BX,.noname+0(FP) 
0005 (temp.go:12) MOVL $0,AX 
0006 (temp.go:12) LEAL .noname+4(FP),DI 
0007 (temp.go:12) STOSL , 
0008 (temp.go:12) STOSL , 
0009 (temp.go:12) RET  , 

--- prog list "myFunction2" --- 
0010 (temp.go:15) TEXT myFunction2+0(SB),$8-12 
0011 (temp.go:16) MOVL $type."".MyStructType+0(SB),(SP) 
0012 (temp.go:16) CALL ,runtime.new+0(SB) 
0013 (temp.go:16) MOVL 4(SP),BX 
0014 (temp.go:18) MOVL BX,.noname+0(FP) 
0015 (temp.go:18) MOVL $0,AX 
0016 (temp.go:18) LEAL .noname+4(FP),DI 
0017 (temp.go:18) STOSL , 
0018 (temp.go:18) STOSL , 
0019 (temp.go:18) RET  , 

Calls

एक समारोह कॉल में, समारोह मूल्य और तर्क सामान्य क्रम में मूल्यांकन कर रहे हैं। उनके मूल्यांकन के बाद, कॉल के पैरामीटर फ़ंक्शन के मान द्वारा पारित किए जाते हैं और कॉल किया गया फ़ंक्शन निष्पादन शुरू होता है।फ़ंक्शन के रिटर्न पैरामीटर मान द्वारा फ़ंक्शन लौटने पर कॉलिंग फ़ंक्शन पर वापस भेजे जाते हैं।

सभी फ़ंक्शन और रिटर्न पैरामीटर मान द्वारा पारित किए जाते हैं। *MyStructType के साथ वापसी पैरामीटर मान एक पता है।

+0

धन्यवाद बहुत बहुत! उपरोक्त, लेकिन मैं भागने के विश्लेषण के बारे में थोड़ी सी वजह से सोनिया को स्वीकार कर रहा हूं। – Joe

+0

पीटर एसओ, आप और @ सोोनिया उस असेंबली का उत्पादन कैसे कर रहे हैं? आप दोनों एक ही स्वरूपण है। मैं इसे कमांड/झंडे के बावजूद उत्पन्न नहीं कर सकता, ओबजडम्प की कोशिश कर रहा था, उपकरण, ओटोल जा रहा था। –

+0

आह, इसे मिला - gcflags। –

10

Go's FAQ के अनुसार:

अगर संकलक साबित नहीं कर सकते कि चर संदर्भित नहीं है समारोह रिटर्न के बाद, तो संकलक चर कचरा-एकत्र ढेर पर सूचक त्रुटियों झूलते से बचने के लिए आवंटित करना चाहिए ।

4

आप हमेशा यह नहीं जानते कि आपका चर ढेर या ढेर पर आवंटित किया गया है या नहीं।
...
यदि आपको यह जानने की आवश्यकता है कि आपके चर आवंटित किए गए हैं तो "-m" gc flag "go build" या "go run" (उदा।, go run -gcflags -m app.go) को पास करें।

स्रोत: http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/index.html#stack_heap_vars

संबंधित मुद्दे