2015-10-22 6 views
15

[यह एक सवाल हाल ही में एक कहीं और चर्चा से प्रेरित है, और मैं इसके साथ सही एक उत्तर प्रदान करेंगे।]सी क्षय में एरे को पॉइंटर्स क्यों करते हैं?

मैं सरणियों के अजीब सी घटना संकेत करने के लिए "खस्ताहाल", उदा के बारे में सोच रहा था जब फ़ंक्शन तर्क के रूप में उपयोग किया जाता है। यह इतना असुरक्षित लगता है। लंबाई के साथ स्पष्ट रूप से पारित करना भी असुविधाजनक है। और मैं अन्य प्रकार के कुल-structs पास कर सकता हूं - मूल्य से पूरी तरह से अच्छी तरह से; structs क्षय नहीं है।

इस डिजाइन निर्णय के पीछे तर्क क्या है? यह भाषा के साथ कैसे एकीकृत करता है? Structs में कोई अंतर क्यों है?

+4

यह पूरी तरह से (अनावश्यक) प्रतिलिपि बचाता है। सी को गति के लिए डिजाइन किया गया था, सुरक्षा नहीं। – Kninnug

+1

क्योंकि तर्कों के रूप में पारित किए गए सरणी की प्रतिलिपि अधिकतर महंगी और अनावश्यक होगी। इसके अलावा, सी मूल रूप से कार्य तर्कों के रूप में उत्तीर्ण structs का समर्थन नहीं करता था, इसलिए structs से सरणी अलग करने के लिए कोई स्पष्ट डिजाइन विकल्प नहीं था। –

+2

@ किन्नुग एक, निश्चित रूप से, अगर भी वांछित हो, तो पते को पास कर सकता है, जैसा कि कुछ और है। –

उत्तर

16

दलील

के समारोह जांच कॉल क्योंकि समस्याओं वहाँ अच्छी तरह से दिखाई दे रहे हैं: क्यों सरणियों बस कार्यों के लिए सरणियों के रूप में, पारित नहीं कर रहे हैं मूल्य द्वारा एक प्रति के रूप में?

पहले एक पूरी तरह से व्यावहारिक कारण है: Arrays बड़ा हो सकता है; उन्हें मूल्य से पारित करने की सलाह नहीं दी जा सकती है क्योंकि वे स्टैक आकार से अधिक हो सकते हैं, खासकर 1 9 70 के दशक में। पहले कंपाइलर पीडीपी -7 पर लगभग 9 केबी रैम के साथ लिखे गए थे।

भाषा में एक और तकनीकी कारण भी है। तर्कों के साथ फ़ंक्शन कॉल के लिए कोड जेनर करना मुश्किल होगा जिसका आकार संकलन समय पर ज्ञात नहीं है। आधुनिक सी में परिवर्तनीय लंबाई सरणी सहित सभी सरणी के लिए, बस कॉल स्टैक पर पते डाल दिए जाते हैं। पता का आकार निश्चित रूप से जाना जाता है। यहां तक ​​कि रन-टाइम आकार की जानकारी वाले विस्तृत सरणी प्रकार वाली भाषाएं भी ढेर पर उचित वस्तुओं को पास नहीं करती हैं। ये भाषाएं आम तौर पर लगभग "हैंडल" पास करती हैं, जो सी ने प्रभावी रूप से 40 वर्षों तक भी किया है। जॉन स्कीट here देखें और एक सचित्र स्पष्टीकरण वह संदर्भित करता है (एसआईसी) here

अब एक भाषा यह एक आवश्यकता बना सकती है कि एक सरणी में हमेशा एक पूर्ण प्रकार हो; यानी जब भी इसका उपयोग किया जाता है, आकार सहित इसकी पूरी घोषणा दिखाई देनी चाहिए। यह सब के बाद, संरचनाओं से सी को क्या आवश्यक है (जब उन्हें एक्सेस किया जाता है)। नतीजतन, संरचनाओं को मूल्य से कार्यों के लिए पारित किया जा सकता है। सरणी के लिए पूर्ण प्रकार की आवश्यकता के साथ-साथ फ़ंक्शन कॉल आसानी से संकलित करने और अतिरिक्त लंबाई तर्कों को पारित करने की आवश्यकता को रोक देगा: sizeof() अभी भी कैली के अंदर अपेक्षित काम करेगा। लेकिन कल्पना करें कि इसका क्या अर्थ है। अगर आकार वास्तव में सरणी के तर्क प्रकार का हिस्सा थे, हम एक सरणी आकार के लिए एक अलग कार्य की आवश्यकता होगी:

// for user input. 
int average_Ten(int arr[10]); 

// for my new Hasselblad. 
int average_ThreeTrillionThreehundredninetythreeBillionNinehundredtwentyeightMillionEighthundredsixthousandfourhundred(int arr[16544*12400]); 
// ... 

तथ्य यह पूरी तरह से गुजर संरचनाओं, जो प्रकार में मतभेद है अगर उनके तत्व अलग करने के लिए तुलनीय होगा में (कहें, 10 इंट तत्वों वाला एक स्ट्रक्चर और 16544 * 12400 वाला एक)। यह स्पष्ट है कि सरणी को अधिक लचीलापन की आवश्यकता है। उदाहरण के लिए, जैसा कि प्रदर्शित किया गया है, आमतौर पर उपयोग करने योग्य लाइब्रेरी फ़ंक्शंस प्रदान नहीं कर सकता जो सरणी तर्क लेता है।

यह "मजबूत टाइपिंग कन्डर्रम" वास्तव में, सी ++ में क्या होता है जब कोई फ़ंक्शन किसी सरणी का संदर्भ लेता है; यही कारण है कि कोई भी ऐसा नहीं करता है, कम से कम स्पष्ट रूप से नहीं। यह उन मामलों के अलावा बेकार होने के बिंदु से पूरी तरह असुविधाजनक है, जो विशिष्ट उपयोगों को लक्षित करते हैं, और जेनेरिक कोड में: सी ++ टेम्पलेट्स संकलन-समय लचीलापन प्रदान करते हैं जो सी

में उपलब्ध है, यदि मौजूदा सी में, वास्तव में सरणी ज्ञात आकार मूल्य से पारित किया जाना चाहिए हमेशा उन्हें एक संरचना में लपेटने की संभावना है। मुझे याद है कि सोलारिस पर कुछ आईपी संबंधित शीर्षकों ने उन्हें पारिवारिक संरचनाओं को उनके साथ सरणी के साथ परिभाषित किया है, जिससे उन्हें प्रतिलिपि बनाने की अनुमति मिलती है।क्योंकि संरचना का बाइट लेआउट तय किया गया था और ज्ञात था, जो समझ में आया।

कुछ पृष्ठभूमि के लिए सी भाषा के विकास का विकास डेनिस रिची द्वारा सी सी के पूर्ववर्ती बीसीपीएल की उत्पत्ति के बारे में कोई सरणी नहीं थी; स्मृति केवल पॉइंटर्स के साथ समरूप रैखिक स्मृति थी।

+0

उत्कृष्ट जवाब के लिए बहुत जटिल है। मैं जो जोड़ूंगा वह यह है कि यह संकेतकों को पॉइंटर्स में "विघटित" करने के लिए समझ में आता है क्योंकि वे अनिवार्य रूप से एक ही चीज़ हैं ... यानी। किसी दिए गए पते से शुरू होने वाली स्मृति में 'sizeof (int) 'बाइट्स को फिर से चलाने का एक तरीका। – DIMMSum

+1

* सीएफ * सिग्नल का दिलचस्प उपयोग, शायद * एकॉर्ड *, * देखें *, या * यह भी देखें * स्कीट के लिंक द्वारा दिए गए प्रत्यक्ष समर्थन के आधार पर थोड़ा और उचित हो सकता है। * सीएफ * एक * अंतर * इंगित करता है * प्राथमिक बिंदु से प्रस्थान * लेकिन एक तुलना वारंट करने के लिए पर्याप्त समान समर्थन। –

+1

हाहा, अपने उदाहरण से प्यार करें। – Himself12794

0

अपनी टाइम मशीन लें और 1 9 70 में वापस यात्रा करें। प्रोग्रामिंग भाषा तैयार करना प्रारंभ करें। आप नीचे दिए गए कोड संकलन और उम्मीद बात करना चाहते हैं: एक ही समय में

size_t i; 
int* p = (int *) malloc (10 * sizeof (int)); 
for (i = 0; i < 10; ++i) p [i] = i; 

int a [10]; 
for (i = 0; i < 10; ++i) a [i] = i; 

, आप एक भाषा है कि सरल है चाहता हूँ। इतना आसान है कि आप इसे 1 9 70 के कंप्यूटर पर संकलित कर सकते हैं। नियम "ए" एक "के पहले तत्व के सूचक" के लिए तय करता है कि वह अच्छी तरह से प्राप्त करता है।

+3

अन्य भाषाएं उस समय (Algol68) कर सकती थीं। यदि आप रिची के पेपर को पढ़ते हैं तो "डिज़ाइन" का पूरा विचार थोड़ा सा लगता है - यह अधिक विकास था ;-) –

10

इस सवाल का जवाब डेनिस रिची का "The Development of the C Language" पत्र में पाया जा सकता है (देखें "भ्रूण सी" खंड)

डेनिस रिची के अनुसार, सी के नवजात संस्करणों सीधे विरासत में मिला है/बी और BCPL भाषाओं से अपनाया सरणी अर्थ विज्ञान - सी के पूर्ववर्ती। उन भाषाओं में सरणी को शाब्दिक रूप से भौतिक पॉइंटर्स के रूप में कार्यान्वित किया गया था। इन पॉइंटर्स ने वास्तविक सरणी तत्वों वाली स्मृति के स्वतंत्र रूप से आवंटित ब्लॉक की ओर इशारा किया। इन पॉइंटर्स को रन टाइम पर शुरू किया गया था। अर्थात। बी और बीसीपीएल दिनों में सरणी को "बाइनरी" (द्विपक्षीय) वस्तुओं के रूप में कार्यान्वित किया गया था: एक स्वतंत्र सूचक डेटा के एक स्वतंत्र ब्लॉक को इंगित करता है। उन भाषाओं में पॉइंटर और सरणी अर्थशास्त्र के बीच कोई अंतर नहीं था, इस तथ्य के अलावा कि सरणी पॉइंटर्स स्वचालित रूप से प्रारंभ किए गए थे। किसी भी समय बी और बीसीपीएल में एक सर पॉइंटर को फिर से असाइन करना संभव था ताकि इसे कहीं और इंगित किया जा सके।

प्रारंभ में, सरणी अर्थशास्त्र के लिए यह दृष्टिकोण सी द्वारा विरासत में मिला। हालांकि, इसकी कमी तब स्पष्ट हो गई जब struct प्रकार भाषा में पेश किए गए थे (कुछ न तो बी और न ही बीसीपीएल था)। और विचार यह था कि structs स्वाभाविक रूप से सरणी रखने में सक्षम होना चाहिए। हालांकि, बी/बीसीपीएल सरणी की उपरोक्त "द्विपक्षीय" प्रकृति के साथ चिपकने के लिए जारी रहना तुरंत structs के साथ कई स्पष्ट जटिलताओं का कारण बन जाएगा। जैसे अंदर के सरणी के साथ संरचना वस्तुओं को परिभाषा के बिंदु पर गैर-तुच्छ "निर्माण" की आवश्यकता होगी। ऐसी संरचना वस्तुओं की प्रतिलिपि बनाना असंभव हो जाएगा - कच्चे memcpy कॉल वास्तविक डेटा की प्रतिलिपि किए बिना सरणी पॉइंटर्स की प्रतिलिपि बनायेगा। malloc संरचना ऑब्जेक्ट्स में सक्षम नहीं होगा, क्योंकि malloc केवल कच्ची मेमोरी आवंटित कर सकता है और किसी भी गैर-तुच्छ प्रारंभिकरण को ट्रिगर नहीं करता है। इत्यादि इत्यादि।

यह अस्वीकार्य समझा जाता था, जिसके कारण सी सरणी का नवीनीकरण हुआ। भौतिक संकेतकों के माध्यम से सरणी को लागू करने के बजाय रिची ने पॉइंटर्स से पूरी तरह से छुटकारा पाने का फैसला किया। नई सरणी को एक तात्कालिक मेमोरी ब्लॉक के रूप में कार्यान्वित किया गया था, जो आज हमारे पास सी में है। हालांकि, पिछड़े संगतता कारणों से बी/बीसीपीएल सरणी के व्यवहार को सतही स्तर पर यथासंभव संरक्षित (नकली) संरक्षित किया गया था: नई सी सर आसानी से क्षय एक अस्थायी सूचक मूल्य पर, सरणी की शुरुआत को इंगित करता है। शेष सरणी कार्यक्षमता अपरिवर्तित बनी रही, जो क्षय के उस आसानी से उपलब्ध परिणाम पर निर्भर है।

समाधान typeless BCPL बीच विकास श्रृंखला में महत्वपूर्ण कूद का गठन और सी टाइप किया यह भंडारण में सूचक की भौतिकीकरण सफाया कर दिया, और बदले के निर्माण की वजह से

ऊपर उल्लिखित कागज के शब्दों में सूचक जब अभिव्यक्ति में सरणी नाम का उल्लेख किया गया है।नियम, जो आज के सी में बचे हैं, यह है कि सरणी प्रकार के मान परिवर्तित होते हैं, जब वे अभिव्यक्ति में दिखाई देते हैं, तो ऑरे बनाने वाले ऑब्जेक्ट्स के पहले पर पॉइंटर्स में परिवर्तित होते हैं।

इस आविष्कार ने भाषा के अर्थशास्त्र में अंतर्निहित बदलाव के बावजूद काम करने के लिए सबसे मौजूदा बी कोड को सक्षम किया है, । कुछ प्रोग्राम जिन्होंने बी 0 और बीसीपीएल में अपने मूल-संभव को समायोजित करने के लिए किसी सरणी नाम पर नए मान असाइन किए हैं, सी-सी में अर्थहीन आसानी से मरम्मत की गई थी। अधिक महत्वपूर्ण, नई भाषा ने एक सुसंगत और व्यावहारिक (अगर असामान्य) सरणी के अर्थशास्त्र की व्याख्या को बनाए रखा, जबकि को एक और व्यापक प्रकार की संरचना में खोलने के दौरान।

तो, का सीधा जवाब अपने "क्यों" सवाल है इस प्रकार है: सी में सरणियों क्रम में संकेत करने के लिए क्षय का अनुकरण बी में सरणियों के ऐतिहासिक व्यवहार (यथासंभव निकट) के लिए डिजाइन किए गए थे और बीसीपीएल भाषाएं

+0

बहुत अच्छी जानकारी। मुझे लगता है कि केवल अब मैं उस मार्ग को समझता हूं जिसे आपने पूरी तरह उद्धृत किया था। –

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