यह पोस्ट का एक अंश है; पूरी पोस्ट यहां उपलब्ध है: गो ऐरे कैसे काम करते हैं और फॉर-रेंज के साथ मुश्किल हो जाते हैं।
क्लासिक गोलांग सरणी और स्लाइस बहुत सीधे हैं। सरणियाँ निश्चित आकार की होती हैं, और स्लाइस गतिशील होती हैं। लेकिन मुझे आपको बताना होगा, गो सतह पर सरल लग सकता है, लेकिन इसमें हुड के नीचे बहुत कुछ चल रहा है।
हमेशा की तरह, हम बुनियादी बातों से शुरुआत करेंगे और फिर थोड़ा और गहराई में जाएंगे। चिंता न करें, जब आप सारणियों को विभिन्न कोणों से देखते हैं तो वे काफी दिलचस्प हो जाती हैं।
हम अगले भाग में स्लाइस को कवर करेंगे, तैयार होने के बाद मैं इसे यहां छोड़ दूंगा।
गो में ऐरे काफी हद तक अन्य प्रोग्रामिंग भाषाओं के समान हैं। उनके पास एक निश्चित आकार है और वे समान प्रकार के तत्वों को सन्निहित स्मृति स्थानों में संग्रहीत करते हैं।
इसका मतलब है कि गो प्रत्येक तत्व तक तुरंत पहुंच सकता है क्योंकि उनके पते की गणना सरणी के शुरुआती पते और तत्व के सूचकांक के आधार पर की जाती है।
func main() { arr := [5]byte{0, 1, 2, 3, 4} println("arr", &arr) for i := range arr { println(i, &arr[i]) } } // Output: // arr 0x1400005072b // 0 0x1400005072b // 1 0x1400005072c // 2 0x1400005072d // 3 0x1400005072e // 4 0x1400005072f
यहाँ ध्यान देने योग्य कुछ बातें हैं:
छवि को ध्यान से देखें।
हमारा स्टैक ऊंचे से निचले पते की ओर नीचे की ओर बढ़ रहा है, है ना? यह चित्र बिल्कुल दिखाता है कि स्टैक में एक सरणी कैसी दिखती है, arr[4] से लेकर arr[0] तक।
तो, क्या इसका मतलब यह है कि हम पहले तत्व (या सरणी) का पता और तत्व के आकार को जानकर किसी सरणी के किसी भी तत्व तक पहुंच सकते हैं? आइए इसे एक पूर्णांक सरणी और असुरक्षित पैकेज के साथ आज़माएँ:
func main() { a := [3]int{99, 100, 101} p := unsafe.Pointer(&a[0]) a1 := unsafe.Pointer(uintptr(p) 8) a2 := unsafe.Pointer(uintptr(p) 16) fmt.Println(*(*int)(p)) fmt.Println(*(*int)(a1)) fmt.Println(*(*int)(a2)) } // Output: // 99 // 100 // 101
ठीक है, हम पहले तत्व के लिए पॉइंटर प्राप्त करते हैं और फिर एक इंट के आकार के गुणकों को जोड़कर अगले तत्वों के लिए पॉइंटर्स की गणना करते हैं, जो 64-बिट आर्किटेक्चर पर 8 बाइट्स है। फिर हम इन पॉइंटर्स का उपयोग उन तक पहुंचने और उन्हें वापस int मानों में बदलने के लिए करते हैं।
उदाहरण शैक्षिक उद्देश्यों के लिए सीधे मेमोरी तक पहुंचने के लिए असुरक्षित पैकेज के साथ एक खिलवाड़ है। परिणामों को समझे बिना उत्पादन में ऐसा न करें।
अब, प्रकार T की एक सरणी अपने आप में एक प्रकार नहीं है, लेकिन एक विशिष्ट आकार और प्रकार T वाली एक सरणी को एक प्रकार माना जाता है। मेरा मतलब यह है:
func main() { a := [5]byte{} b := [4]byte{} fmt.Printf("%T\n", a) // [5]uint8 fmt.Printf("%T\n", b) // [4]uint8 // cannot use b (variable of type [4]byte) as [5]byte value in assignment a = b }
हालाँकि a और b दोनों बाइट्स की सरणियाँ हैं, गो कंपाइलर उन्हें पूरी तरह से अलग प्रकार के रूप में देखता है, %T प्रारूप इस बिंदु को स्पष्ट करता है।
यहां बताया गया है कि गो कंपाइलर इसे आंतरिक रूप से कैसे देखता है (src/cmd/compile/internal/types2/array.go):
// An Array represents an array type. type Array struct { len int64 elem Type } // NewArray returns a new array type for the given element type and length. // A negative length indicates an unknown length. func NewArray(elem Type, len int64) *Array { return &Array{len: len, elem: elem} }
सरणी की लंबाई प्रकार में ही "एन्कोडेड" होती है, इसलिए कंपाइलर सरणी की लंबाई को उसके प्रकार से जानता है। एक आकार की सरणी को दूसरे आकार में निर्दिष्ट करने या उनकी तुलना करने का प्रयास करने पर बेमेल प्रकार की त्रुटि उत्पन्न होगी।
गो में किसी ऐरे को आरंभ करने के कई तरीके हैं, और उनमें से कुछ का उपयोग वास्तविक परियोजनाओं में शायद ही कभी किया जा सकता है:
var arr1 [10]int // [0 0 0 0 0 0 0 0 0 0] // With value, infer-length arr2 := [...]int{1, 2, 3, 4, 5} // [1 2 3 4 5] // With index, infer-length arr3 := [...]int{11: 3} // [0 0 0 0 0 0 0 0 0 0 0 3] // Combined index and value arr4 := [5]int{1, 4: 5} // [1 0 0 0 5] arr5 := [5]int{2: 3, 4, 4: 5} // [0 0 3 4 5]
हम ऊपर जो कर रहे हैं (पहले वाले को छोड़कर) वह दोनों उनके मूल्यों को परिभाषित और आरंभ कर रहा है, जिसे "समग्र शाब्दिक" कहा जाता है। इस शब्द का उपयोग स्लाइस, मानचित्र और संरचना के लिए भी किया जाता है।
अब, यहां एक दिलचस्प बात है: जब हम 4 से कम तत्वों के साथ एक सरणी बनाते हैं, तो गो मानों को एक-एक करके सरणी में डालने के लिए निर्देश उत्पन्न करता है।
इसलिए जब हम arr := [3]int{1, 2, 3, 4} करते हैं, तो वास्तव में क्या हो रहा है:
arr := [4]int{} arr[0] = 1 arr[1] = 2 arr[2] = 3 arr[3] = 4
इस रणनीति को स्थानीय-कोड आरंभीकरण कहा जाता है। इसका मतलब यह है कि इनिशियलाइज़ेशन कोड वैश्विक या स्थैतिक इनिशियलाइज़ेशन कोड का हिस्सा होने के बजाय एक विशिष्ट फ़ंक्शन के दायरे में उत्पन्न और निष्पादित होता है।
जब आप नीचे एक और आरंभीकरण रणनीति पढ़ेंगे तो यह स्पष्ट हो जाएगा, जहां मानों को इस तरह एक-एक करके सरणी में नहीं रखा जाता है।
"4 से अधिक तत्वों वाले सरणियों के बारे में क्या?"
कंपाइलर बाइनरी में सरणी का एक स्थिर प्रतिनिधित्व बनाता है, जिसे 'स्थिर आरंभीकरण' रणनीति के रूप में जाना जाता है।
इसका मतलब है कि सरणी तत्वों के मान बाइनरी के केवल पढ़ने योग्य अनुभाग में संग्रहीत हैं। यह स्थिर डेटा संकलन समय पर बनाया जाता है, इसलिए मान सीधे बाइनरी में एम्बेडेड होते हैं। यदि आप जानना चाहते हैं कि गो असेंबली में [5]int{1,2,3,4,5} कैसा दिखता है:
main..stmp_1 SRODATA static size=40 0x0000 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ 0x0010 03 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ................ 0x0020 05 00 00 00 00 00 00 00 ........
सरणी का मूल्य देखना आसान नहीं है, फिर भी हम इससे कुछ महत्वपूर्ण जानकारी प्राप्त कर सकते हैं।
हमारा डेटा stmp_1 में संग्रहीत है, जो 40 बाइट्स (प्रत्येक तत्व के लिए 8 बाइट्स) के आकार के साथ केवल पढ़ने योग्य स्थिर डेटा है, और इस डेटा का पता बाइनरी में हार्डकोड किया गया है।
संकलक इस स्थिर डेटा को संदर्भित करने के लिए कोड उत्पन्न करता है। जब हमारा एप्लिकेशन चलता है, तो यह सरणी सेट करने के लिए अतिरिक्त कोड की आवश्यकता के बिना सीधे इस पूर्व-प्रारंभिक डेटा का उपयोग कर सकता है।
const readonly = [5]int{1, 2, 3, 4, 5} arr := readonly
"उस सरणी के बारे में क्या कहें जिसमें 5 तत्व हैं लेकिन उनमें से केवल 3 ही आरंभ हुए हैं?"
अच्छा सवाल है, यह शाब्दिक [5]int{1,2,3} पहली श्रेणी में आता है, जहां गो एक-एक करके सरणी में मान डालता है।
सरणी को परिभाषित करने और आरंभ करने के बारे में बात करते समय, हमें यह उल्लेख करना चाहिए कि प्रत्येक सरणी को स्टैक पर आवंटित नहीं किया जाता है। यदि यह बहुत बड़ा है, तो इसे ढेर में ले जाया जाता है।
लेकिन "बहुत बड़ा" कितना बड़ा है, आप पूछ सकते हैं।
गो 1.23 के अनुसार, यदि वेरिएबल का आकार, न कि केवल सरणी, एक स्थिर मान MaxStackVarSize से अधिक है, जो वर्तमान में 10 एमबी है, तो इसे स्टैक आवंटन के लिए बहुत बड़ा माना जाएगा और ढेर में भाग जाएगा।
func main() { a := [10 * 1024 * 1024]byte{} println(&a) b := [10*1024*1024 1]byte{} println(&b) }
इस परिदृश्य में, बी ढेर में चला जाएगा जबकि ए नहीं।
सरणी की लंबाई प्रकार में ही एन्कोड की गई है। भले ही सरणियों में कैप प्रॉपर्टी नहीं है, फिर भी हम इसे प्राप्त कर सकते हैं:
func main() { a := [5]int{1, 2, 3} println(len(a)) // 5 println(cap(a)) // 5 }
क्षमता लंबाई के बराबर होती है, इसमें कोई संदेह नहीं है, लेकिन सबसे महत्वपूर्ण बात यह है कि हम इसे संकलन समय पर जानते हैं, है ना?
इसलिए len(a) का कंपाइलर के लिए कोई मतलब नहीं है क्योंकि यह रनटाइम प्रॉपर्टी नहीं है, गो कंपाइलर कंपाइल समय पर मूल्य जानता है।
...
यह पोस्ट का एक अंश है; पूरी पोस्ट यहां उपलब्ध है: गो ऐरे कैसे काम करते हैं और फॉर-रेंज के साथ मुश्किल हो जाते हैं।
अस्वीकरण: उपलब्ध कराए गए सभी संसाधन आंशिक रूप से इंटरनेट से हैं। यदि आपके कॉपीराइट या अन्य अधिकारों और हितों का कोई उल्लंघन होता है, तो कृपया विस्तृत कारण बताएं और कॉपीराइट या अधिकारों और हितों का प्रमाण प्रदान करें और फिर इसे ईमेल पर भेजें: [email protected] हम इसे आपके लिए यथाशीघ्र संभालेंगे।
Copyright© 2022 湘ICP备2022001581号-3