C सरणियाँ उनकी लंबाई का ट्रैक क्यों नहीं रखती हैं?


77

किसी सरणी की लंबाई को स्पष्ट रूप से एक सरणी के साथ संग्रहीत करने के पीछे तर्क क्या था C?

जिस तरह से मैं इसे देखता हूं, ऐसा करने के लिए बहुत सारे कारण हैं लेकिन मानक (C89) के समर्थन में बहुत सारे नहीं हैं। उदाहरण के लिए:

  1. एक बफर में लंबाई उपलब्ध होने से बफर ओवररन को रोका जा सकता है।
  2. एक जावा-शैली arr.lengthदोनों स्पष्ट है और प्रोग्रामर को intकई सरणियों से निपटने पर स्टैक पर कई एस बनाए रखने से बचाती है
  3. फंक्शन पैरामीटर अधिक अस्पष्ट हो जाते हैं।

लेकिन शायद मेरी राय में, सबसे प्रेरक कारण यह है कि आमतौर पर, कोई भी स्थान लंबाई को बनाए बिना बचा रहता है। मैं यह कहना चाहूंगा कि सरणियों के अधिकांश उपयोगों में गतिशील आवंटन शामिल है। सच है, ऐसे कुछ मामले हो सकते हैं जहां लोग स्टैक पर आवंटित एक सरणी का उपयोग करते हैं, लेकिन यह सिर्फ एक फ़ंक्शन कॉल * है - स्टैक 4 या 8 बाइट्स को अतिरिक्त रूप से संभाल सकता है।

चूंकि हीप मैनेजर को डायनेमिक रूप से आवंटित एरे द्वारा उपयोग किए जाने वाले फ्री ब्लॉक साइज को ट्रैक करना होता है, इसलिए उस सूचना को प्रयोग करने योग्य नहीं बनाया जाए (और अतिरिक्त नियम को जोड़ा जाए, संकलन समय पर जांचा जाए, कि जब तक कोई स्पष्ट रूप से लंबाई में हेरफेर नहीं कर सकता। पैर में अपने आप को गोली मारना पसंद है)।

दूसरी तरफ मैं केवल एक चीज सोच सकता हूं कि किसी भी लम्बाई पर नज़र रखने वाले ने संकलक को सरल नहीं बनाया है, लेकिन इतना सरल नहीं है

* तकनीकी रूप से, कोई स्वचालित भंडारण के साथ एक सरणी के साथ कुछ प्रकार के पुनरावर्ती कार्य लिख सकता है, और इस (बहुत विस्तृत) मामले में लंबाई को संग्रहीत करने से वास्तव में अधिक स्थान उपयोग हो सकता है।


6
मुझे लगता है कि यह तर्क दिया जा सकता है कि जब सी ने पैरामीटर और रिटर्न वैल्यू टाइप के रूप में स्ट्रक्चर्स का उपयोग किया था, तो इसमें "वैक्टर" (या जो भी नाम) के लिए सिंटैक्टिक शुगर शामिल होना चाहिए था, जो लंबाई और या तो सरणी या पॉइंटर के साथ संरचना के नीचे होगा। । इस सामान्य निर्माण के लिए भाषा स्तर का समर्थन (यह भी जब अलग-अलग तर्कों और एकल संरचना के रूप में पारित किया गया है) ने अनगिनत बगों को बचाया होगा और मानक पुस्तकालय को भी सरल बनाया होगा।
हाइड

3
तुम भी पा सकते हैं क्यों पास्कल मेरी पसंदीदा प्रोग्रामिंग भाषा नहीं है धारा 2.1 व्यावहारिक होने के लिए।

34
जबकि अन्य सभी उत्तरों में कुछ दिलचस्प बिंदु हैं, मुझे लगता है कि निचली रेखा यह है कि सी को लिखा गया था ताकि असेंबली भाषा प्रोग्रामर कोड को आसानी से लिख सकें और यह पोर्टेबल हो। उस के साथ, एक सरणी लंबाई के साथ एक सरणी के साथ संग्रहीत स्वचालित रूप से एक उपद्रव होता है और एक कमी नहीं (जैसा कि अन्य अच्छा कैंडी कोटिंग इच्छाओं होगा)। ये सुविधाएँ आजकल बहुत अच्छी लगती हैं, लेकिन फिर यह वास्तव में अक्सर आपके सिस्टम में प्रोग्राम या डेटा के एक और बाइट को निचोड़ने के लिए संघर्ष था। स्मृति के व्यर्थ उपयोग ने सी की गोद को गंभीर रूप से सीमित कर दिया होगा।
डंक

6
आपके उत्तर का वास्तविक हिस्सा पहले ही कई बार मेरे द्वारा बताए गए तरीके से उत्तर दिया जा चुका है, लेकिन मैं एक अलग बिंदु निकाल सकता हूं: "एक malloc()एड क्षेत्र का आकार पोर्टेबल तरीके से क्यों नहीं अनुरोध किया जा सकता है?" यह एक ऐसी बात है जो मुझे कई बार हैरान करती है।
ग्लोगल

5
फिर से मतदान करना। कहीं न कहीं कोई कारण है, भले ही यह "K & R ने इसके बारे में नहीं सोचा है"।
तेलस्टिन

जवाबों:


106

C सरणियाँ उनकी लंबाई का ट्रैक रखती हैं, क्योंकि सरणी लंबाई एक स्थिर गुण है:

int xs[42];  /* a 42-element array */

आप आमतौर पर इस लंबाई को क्वेरी नहीं कर सकते, लेकिन आपको इसकी आवश्यकता नहीं है क्योंकि यह वैसे भी स्थिर है - बस XS_LENGTHलंबाई के लिए एक मैक्रो घोषित करें , और आप कर रहे हैं।

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

एक और बात यह है कि पॉइंटर्स को मेमोरी एड्रेस के अलावा कोई स्टोरेज नहीं चाहिए। C हमें पॉइंटर्स को पॉइंटर्स, दूसरे पॉइंटर्स को पॉइंटर्स और पॉइंटर्स को ट्रीट करने के लिए देता है जैसे कि वे एरेज़ थे। ऐसा करते समय, सी पर्याप्त नहीं है कि कुछ सरणी लंबाई को अस्तित्व में बनाया जा सके, लेकिन स्पाइडरमैन आदर्श वाक्य में भरोसा करना पड़ता है: महान शक्ति के साथ प्रोग्रामर उम्मीद करेगा कि लंबाई और ओवरफ्लो का ध्यान रखें।


13
मुझे लगता है कि आपके कहने का मतलब है, अगर मैं गलत नहीं हूं, तो सी संकलक स्थिर सरणी लंबाई का ट्रैक रखता है। लेकिन यह उन कार्यों के लिए अच्छा नहीं है जो केवल एक संकेतक प्राप्त करते हैं।
VF1

25
@ VF1 हाँ। लेकिन महत्वपूर्ण बात यह है कि सी में सरणियां और संकेत अलग-अलग चीजें हैं । मान लें कि आप किसी संकलक एक्सटेंशन का उपयोग नहीं कर रहे हैं, तो आप आम तौर पर किसी फ़ंक्शन के लिए एक सरणी को पास नहीं कर सकते, लेकिन आप एक पॉइंटर को पास कर सकते हैं, और एक पॉइंटर को इंडेक्स कर सकते हैं जैसे कि यह एक एरे थे। आप प्रभावी ढंग से शिकायत कर रहे हैं कि पॉइंटर्स की कोई लम्बाई नहीं है। आपको शिकायत करनी चाहिए कि सरणियों को फ़ंक्शन तर्कों के रूप में पारित नहीं किया जा सकता है, या यह कि सरणियों को अव्यवस्थित रूप से इंगित करता है।
अमोन

37
"आप आमतौर पर इस लंबाई को क्वेरी नहीं कर सकते हैं" - वास्तव में आप यह कर सकते हैं, यह आकार का ऑपरेटर है - sizeof (xs) 168 मान लेंगे कि इंट चार बाइट्स लंबे हैं। 42 पाने के लिए, do: sizeof (xs) / sizeof (int)
tcrosley

15
@tcrosley जो केवल ऐरे डिक्लेरेशन के दायरे में काम करता है - एक अन्य फंक्शन के लिए एक परम के रूप में xs पास करने की कोशिश करें फिर देखें कि क्या आकार (xs) आपको देता है ...
Gwyn Evans

26
@GwynEvans फिर से: संकेत सरणियाँ नहीं हैं। इसलिए यदि आप "किसी अन्य फ़ंक्शन के लिए एक सरणी को पैराम के रूप में पास करते हैं", तो आप एक सरणी नहीं बल्कि एक पॉइंटर पास कर रहे हैं। यह दावा करना कि एक सरणी sizeof(xs)कहां xsहोगी एक और गुंजाइश में कुछ अलग होगा यह स्पष्ट रूप से गलत है, क्योंकि सी का डिज़ाइन सरणियों को अपना दायरा छोड़ने की अनुमति नहीं देता है। तो sizeof(xs)जहां xsहै एक सरणी से अलग है sizeof(xs)जहां xsएक सूचक है, कि क्योंकि आप कर रहे हैं कोई आश्चर्य की बात के रूप में आता संतरे के साथ सेब की तुलना
अमन

38

इसका बहुत कुछ उस समय उपलब्ध कंप्यूटरों के साथ करना था। न केवल संकलित कार्यक्रम को एक सीमित संसाधन कंप्यूटर पर चलना था, बल्कि, शायद इससे भी महत्वपूर्ण बात यह है कि संकलक को इन मशीनों पर चलना था। जिस समय थॉम्पसन ने C विकसित किया, वह 8k RAM के साथ PDP-7 का उपयोग कर रहा था। वास्तविक मशीन कोड पर एक तत्काल एनालॉग नहीं होने वाली जटिल भाषा सुविधाएँ बस भाषा में शामिल नहीं थीं।

सी के इतिहास के माध्यम से एक सावधान पढ़ने के लिए उपरोक्त में अधिक समझ पैदा करता है, लेकिन यह पूरी तरह से मशीन की सीमाओं का परिणाम नहीं था:

इसके अलावा, भाषा (सी) महत्वपूर्ण अवधारणाओं का वर्णन करने के लिए काफी शक्ति दिखाती है, उदाहरण के लिए, वैक्टर जिनकी लंबाई रन टाइम में बदलती है, केवल कुछ बुनियादी नियमों और सम्मेलनों के साथ। ... सी के दृष्टिकोण की तुलना लगभग दो समकालीन भाषाओं, अल्गोल 68 और पास्कल [जेन्स 74] के साथ करना दिलचस्प है। अल्गोल 68 में एरर्स के पास या तो निश्चित सीमाएँ हैं, या `लचीले: 'भाषा की परिभाषा में और संकलक दोनों में, लचीले सरणियों को समायोजित करने के लिए (और सभी कंपाइलर उन्हें पूरी तरह से लागू नहीं करते हैं) के लिए काफी तंत्र की आवश्यकता होती है। मूल पास्कल केवल निश्चित आकार के होते हैं। सरणियाँ और तार, और यह सीमित साबित हुआ [कर्निघन 81]।

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


4
यह बहुत ज्यादा मूल सवाल नाखून। वह तथ्य यह है कि सी को जानबूझकर "हल्का स्पर्श" रखा जा रहा था जब यह जाँचने के लिए आया था कि प्रोग्रामर क्या कर रहा है, इसे ऑपरेटिंग सिस्टम लिखने के लिए आकर्षक बनाने के हिस्से के रूप में।
क्लिक करें

5
महान लिंक, उन्होंने स्पष्ट रूप से एक परिसीमन का उपयोग करने के लिए तार की लंबाई को संग्रहीत करते हुए बदल दिया to avoid the limitation on the length of a string caused by holding the count in an 8- or 9-bit slot, and partly because maintaining the count seemed, in our experience, less convenient than using a terminator- अच्छी तरह से इसके लिए बहुत कुछ :-)
वू

5
सी के नंगे धातु दृष्टिकोण के साथ असमान सरणियाँ भी फिट बैठती हैं। याद रखें कि के एंड आर सी पुस्तक भाषा ट्यूटोरियल, संदर्भ और मानक कॉल की सूची के साथ 300 से कम पृष्ठ है। मेरी ओ रीली रेगेक्स किताब K & R C. से लगभग दोगुनी लंबी है
माइकल शोपिन

22

दिन में वापस जब सी बनाया गया था, और हर स्ट्रिंग के लिए अतिरिक्त 4 बाइट्स की जगह चाहे कितनी भी कम क्यों न हो, काफी बर्बादी होगी!

एक और मुद्दा है - याद रखें कि सी ऑब्जेक्ट-ओरिएंटेड नहीं है, इसलिए यदि आप सभी तारों को लंबाई-उपसर्ग करते हैं, तो इसे संकलक आंतरिक प्रकार के रूप में परिभाषित करना होगा, न कि ए char*। यदि यह एक विशेष प्रकार था, तो आप एक स्ट्रिंग की निरंतर स्ट्रिंग से तुलना करने में सक्षम नहीं होंगे, अर्थात:

String x = "hello";
if (strcmp(x, "hello") == 0) 
  exit;

विशेष संकलक विवरण के लिए या तो उस स्थिर स्ट्रिंग को स्ट्रिंग में बदलना होगा, या लंबाई के उपसर्ग को ध्यान में रखने के लिए अलग-अलग स्ट्रिंग फ़ंक्शन होंगे।

मुझे लगता है कि अंततः, वे सिर्फ पास्कल के विपरीत लंबाई-उपसर्ग का रास्ता नहीं चुनते थे।


10
सीमा की जाँच में भी समय लगता है। आज के संदर्भ में तुच्छ, लेकिन कुछ लोगों ने ध्यान दिया जब उन्होंने 4 बाइट्स का ध्यान रखा।
स्टीवन बर्नैप

18
@StevenBurnap: यदि आप एक आंतरिक लूप में हैं जो आज भी 200 एमबी की छवि के हर पिक्सेल के ऊपर जाता है, तो यह तुच्छ नहीं है। सामान्य तौर पर, यदि आप C लिख रहे हैं तो आप तेजी से जाना चाहते हैं , और जब आप अपने forपाश को सीमाओं का सम्मान करने के लिए पहले से ही सेट कर लिया गया था, तो आप हर पुनरावृत्ति पर बेकार बाउंड चेक में समय बर्बाद नहीं करना चाहते हैं।
मट्टियो इटालिया

4
@ VF1 "दिन में वापस" यह दो बाइट्स हो सकता है (DEC PDP / 11 किसी?)
ClickRick

7
इसका सिर्फ "दिन में वापस" नहीं है। सॉफ़्टवेयर के लिए जिसे C को "पोर्टेबल असेंबली लैंग्वेज" जैसे OS kernals, डिवाइस ड्राइवर, एम्बेडेड रियल टाइम सॉफ़्टवेयर इत्यादि के रूप में लक्षित किया गया है। सीमा जाँच पर आधा दर्जन निर्देशों को बर्बाद करना मायने रखता है, और, कई मामलों में आपको "सीमा से बाहर" होने की आवश्यकता है (आप एक डिबगर कैसे लिख सकते हैं यदि आप बेतरतीब ढंग से दूसरे कार्यक्रमों के भंडारण तक नहीं पहुंच सकते हैं?)।
जेम्स एंडरसन

3
यह वास्तव में एक कमजोर तर्क है, इस पर विचार करते हुए कि BCPL के पास लम्बित तर्क थे। पास्कल के रूप में हालांकि यह 1 शब्द तक सीमित था, इसलिए आम तौर पर केवल 8 या 9 बिट्स था, जो थोड़ा सीमित था (यह स्ट्रिंग्स के कुछ हिस्सों को साझा करने की संभावना को भी रोकता है, हालांकि यह अनुकूलन शायद समय के लिए बहुत उन्नत था)। और एक स्ट्रिंग को एक लंबाई के साथ एक संरचना के रूप में घोषित करने के बाद सरणी को वास्तव में विशेष संकलक समर्थन की आवश्यकता नहीं होगी ..
वू

11

C में, किसी सरणी का कोई सन्निहित सबसेट भी एक सरणी है और इसे इस प्रकार संचालित किया जा सकता है। यह संचालन को पढ़ने और लिखने दोनों पर लागू होता है। यदि आकार स्पष्ट रूप से संग्रहीत किया गया था तो यह संपत्ति धारण नहीं करेगी।


6
"डिजाइन अलग होगा" डिजाइन अलग होने के खिलाफ एक कारण नहीं है।
VF1

7
@ VF1: क्या आपने कभी स्टैंडर्ड पास्कल में प्रोग्राम किया है? सरणियों के साथ उचित रूप से लचीली होने की सी क्षमता असेंबली (कोई सुरक्षा नहीं है) और प्रकार की भाषाओं की पहली पीढ़ी पर भारी सुधार था (सटीक सरणी सीमाएं सहित ओवरकिलill)
MSalters

5
किसी सरणी को स्लाइस करने की क्षमता वास्तव में C89 डिज़ाइन के लिए एक बड़े पैमाने पर तर्क है।

पुराने स्कूल फोरट्रान हैकर्स ने भी इस संपत्ति का अच्छा उपयोग किया है (हालांकि, इसके लिए फोरट्रान में एक सरणी को स्लाइस पास करना आवश्यक है)। भ्रमित और प्रोग्राम या डिबग के लिए दर्दनाक, लेकिन काम करते समय तेज और सुरुचिपूर्ण।
dmckee

3
एक दिलचस्प डिज़ाइन विकल्प है जो स्लाइसिंग की अनुमति देता है: सरणियों के साथ लंबाई को स्टोर न करें। किसी भी पॉइंटर के लिए एक ऐरे के लिए, पॉइंटर से लंबाई को स्टोर करें। (जब आपके पास बस एक वास्तविक सी सरणी होती है, तो आकार एक संकलन समय स्थिर और संकलक के लिए उपलब्ध होता है।) यह अधिक स्थान लेता है, लेकिन लंबाई बनाए रखते हुए स्लाइस करने की अनुमति देता है। &[T]उदाहरण के लिए, जंग इस प्रकार करता है ।

8

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

void ClearTwoElements(int *ptr)
{
  ptr[-2] = 0;
  ptr[2] = 0;
}
void blah(void)
{
  static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
  ClearTwoElements(foo+2);
  ClearTwoElements(foo+7);
  ClearTwoElements(foo+1);
  ClearTwoElements(foo+8);
}

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

void ClearTwoElements(int *ptr)
{
  int* array_end = ARRAY_END(ptr);
  if ((array_end - ARRAY_BASE(ptr)) < 10 ||
      (ARRAY_BASE(ptr)+4) <= ADDRESS(ptr) ||          
      (array_end - 4) < ADDRESS(ptr)))
    trap();
  *(ADDRESS(ptr) - 4) = 0;
  *(ADDRESS(ptr) + 4) = 0;
}

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

void ClearTwoElements(int arr[], int index)
{
  arr[index-2] = 0;
  arr[index+2] = 0;
}
void blah(void)
{
  static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
  ClearTwoElements(foo,2);
  ClearTwoElements(foo,7);
  ClearTwoElements(foo,1);
  ClearTwoElements(foo,8);
}

एक प्रकार की अवधारणा जो किसी वस्तु को पहचानने के लिए किसी वस्तु को पहचानने के लिए किसी चीज को जोड़ती है वह एक अच्छा है। एक सी-स्टाइल पॉइंटर तेज़ है, हालाँकि, यदि सत्यापन करना आवश्यक नहीं है।


यदि सरणियों में रनटाइम आकार होता है, तो पॉइंटर से सरणी के तत्व से सूचक से मौलिक रूप से भिन्न होगा। हो सकता है कि लैटर सीधे पूर्व में बिल्कुल भी परिवर्तनीय न हो (नया एरे बनाए बिना)। []सिंटैक्स अभी भी पॉइंटर्स के लिए मौजूद हो सकता है, लेकिन यह इन काल्पनिक "वास्तविक" सरणियों से भिन्न होगा, और आपके द्वारा वर्णित समस्या शायद मौजूद नहीं होगी।
हाइड

@ प्रेस: ​​सवाल यह है कि क्या अंकगणित को उन बिंदुओं पर अनुमति दी जानी चाहिए जिनका ऑब्जेक्ट आधार पता अज्ञात है। इसके अलावा, मैं एक और कठिनाई भूल गया: संरचनाओं के भीतर सरणियाँ। इसके बारे में सोचते हुए, मुझे यकीन नहीं है कि कोई भी सूचक प्रकार होगा जो संरचना के भीतर संग्रहीत एक सरणी को इंगित कर सकता है, प्रत्येक सूचक को न केवल सूचक के पते को शामिल करने की आवश्यकता के बिना, बल्कि ऊपरी और निचले कानूनी भी पर्वतमाला इसे एक्सेस कर सकती है।
सुपरकाट

बिंदास बिंदु। मुझे लगता है कि यह अभी भी अमोन के जवाब को कम करता है, हालांकि।
VF1

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

@ प्रेस: ​​यदि किसी ने भाषा के शब्दांश को पर्याप्त रूप से बदल दिया है, तो संभव है कि सरणियों में एक संबद्ध लंबाई शामिल हो, हालांकि संरचनाओं के भीतर संग्रहीत सरणियों में कुछ कठिनाइयां होंगी। शब्दार्थ के रूप में वे कर रहे हैं, सरणी सीमा-जाँच केवल उपयोगी होगा अगर वही जाँच सरणी तत्वों को संकेत करने के लिए लागू किया जाता है।
सुपरकाट

7

सी और अधिकांश अन्य 3 पीढ़ी की भाषाओं के बीच के विभिन्न अंतरों में से एक, और उन सभी अधिक हाल की भाषाओं के बारे में जिनसे मैं परिचित हूं, यह है कि सी को प्रोग्रामर के लिए जीवन को आसान या सुरक्षित बनाने के लिए नहीं बनाया गया था। यह इस उम्मीद के साथ डिजाइन किया गया था कि प्रोग्रामर जानता था कि वे क्या कर रहे थे और केवल और केवल यही करना चाहते थे। यह does पर्दे के पीछे ’कुछ नहीं करता है इसलिए आपको कोई आश्चर्य नहीं होता है। यहां तक ​​कि संकलक स्तर का अनुकूलन वैकल्पिक है (जब तक कि आप एक Microsoft संकलक का उपयोग नहीं करते हैं)।

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


3
सी इतना "डिज़ाइन" नहीं किया गया था क्योंकि यह विकसित हुआ था। मूल रूप से, एक घोषणा की तरह एक पांच आइटम सरणी के रूप में int f[5];नहीं बनाएगा f; इसके बजाय, यह बराबर था int CANT_ACCESS_BY_NAME[5]; int *f = CANT_ACCESS_BY_NAME;। पूर्व की घोषणा को संकलक के बिना संसाधित किया जा सकता है, जो वास्तव में "सरणी" समय को समझता है; यह बस अंतरिक्ष आवंटित करने के लिए एक कोडांतरक निर्देश उत्पादन करने के लिए किया था और फिर भूल सकता है कि fकभी भी एक सरणी के साथ कुछ भी करना था। सरणी प्रकार के असंगत व्यवहार इसी से उपजा है।
सुपरकैट

1
पता चला कि कोई भी प्रोग्रामर नहीं जानता कि वे उस डिग्री के लिए क्या कर रहे हैं जिसे सी की आवश्यकता है।
कोडइन्चोस

7

संक्षिप्त जवाब:

क्योंकि C एक निम्न-स्तरीय प्रोग्रामिंग भाषा है, इसलिए यह आपसे इन मुद्दों की देखभाल करने की अपेक्षा करता है, लेकिन यह वास्तव में इसे लागू करने के तरीके में अधिक लचीलापन जोड़ता है।

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

retval = my_func(my_array, my_array_length);

या आप एक पॉइंटर और लंबाई या किसी अन्य समाधान के साथ एक संरचना का उपयोग कर सकते हैं।

एक उच्च स्तरीय भाषा आपके लिए इसके प्रकार के भाग के रूप में काम करेगी। C में आपको इसे स्वयं करने की ज़िम्मेदारी दी गई है, लेकिन यह भी चुनने का लचीलापन है। और अगर आप जो भी कोड लिख रहे हैं, वह पहले से ही सरणी की लंबाई जानता है, तो आपको एक चर के रूप में लंबाई को पास करने की आवश्यकता नहीं है।

स्पष्ट दोष यह है कि एरियर्स पर कोई अंतर्निहित सीमा की जाँच के संकेत के रूप में चारों ओर से गुजरते हुए आप कुछ खतरनाक कोड बना सकते हैं, लेकिन यह निम्न स्तर / सिस्टम भाषाओं और उनके द्वारा दिए जाने वाले ट्रेड-ऑफ की प्रकृति है।


1
+1 "और यदि आप जो सभी कोड लिख रहे हैं, वह पहले से ही सरणी की लंबाई जानता है, तो आपको एक चर के रूप में लंबाई को पास करने की आवश्यकता नहीं है।"
林果 林果

यदि केवल सूचक + लंबाई संरचना भाषा और मानक पुस्तकालय में बेक किया गया है। इतने सारे सुरक्षा छिद्रों से बचा जा सकता था।
कोडइन्चोस

तब यह वास्तव में नहीं होगा। सी अन्य भाषाएं हैं जो ऐसा करती हैं। C आपको निम्न स्तर का हो जाता है।
thomasrutter

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

5

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

एक बड़ी समस्या यह है कि लंबाई कहाँ रखी जाए और इसे कब तक बनाया जाए। एक जगह नहीं है जो सभी स्थितियों में काम करती है। आप कह सकते हैं कि डेटा से ठीक पहले मेमोरी में लंबाई को स्टोर करें। यदि सरणी मेमोरी की ओर इशारा नहीं कर रही है, लेकिन UART बफर की तरह कुछ है?

लंबाई को छोड़ने से प्रोग्रामर को उपयुक्त स्थिति के लिए अपने स्वयं के अमूर्त बनाने की अनुमति मिलती है, और सामान्य प्रयोजन के मामले के लिए बहुत सारे तैयार पुस्तकालय उपलब्ध हैं। असली सवाल यह है कि सुरक्षा-संवेदनशील अनुप्रयोगों में उन अमूर्तियों का उपयोग क्यों नहीं किया जा रहा है ?


1
You might say just store the length in the memory just before the data. What if the array isn't pointing to memory, but something like a UART buffer?क्या आप इसे थोड़ा और समझा सकते हैं? यह भी कि कुछ ऐसा जो अक्सर हो सकता है या यह सिर्फ एक दुर्लभ मामला है?
महदी

अगर मैंने इसे डिज़ाइन किया था, तो एक फ़ंक्शन तर्क के रूप में लिखा गया था, लेकिन यह फ़ंक्शन के लिए सूचक और आकार का एक गुच्छे के T[]बराबर नहीं होगा T*। फिक्स्ड साइज एरेज ऐसे एरो स्लाइस को क्षय कर सकते हैं, जो पॉइंटर्स को डिकॉय करने के बजाय सी में करते हैं। इस एप्रोच का मुख्य लाभ यह नहीं है कि यह खुद से सुरक्षित है, बल्कि यह एक ऐसा कन्वेंशन है, जिस पर स्टैण्डर्ड लाइब्रेरी सहित हर चीज हो सकती है। निर्माण।
कोडइन्चोअस

1

से सी भाषा का विकास :

संरचनाएं, ऐसा लग रहा था, मशीन में स्मृति पर सहज तरीके से नक्शा करना चाहिए, लेकिन एक सरणी में संरचना में, सरणी के आधार वाले सूचक को स्टैश करने के लिए कोई अच्छी जगह नहीं थी, न ही यह व्यवस्था करने के लिए कोई सुविधाजनक तरीका। प्रारंभ। उदाहरण के लिए, प्रारंभिक यूनिक्स प्रणालियों की निर्देशिका प्रविष्टियों को सी के रूप में वर्णित किया जा सकता है
struct {
    int inumber;
    char    name[14];
};
मैं चाहता था कि संरचना न केवल एक अमूर्त वस्तु को चिह्नित करने के लिए बल्कि बिट्स के एक संग्रह का वर्णन करने के लिए भी है जो एक निर्देशिका से पढ़ी जा सकती है। संकलक पॉइंटर को कहां तक ​​छिपा सकता है nameकि शब्दार्थ की मांग की जाए? यहां तक ​​कि अगर संरचनाओं को अधिक अमूर्त के बारे में सोचा गया था, और पॉइंटर्स के लिए स्थान किसी भी तरह से छिपाया जा सकता है, तो मैं एक जटिल वस्तु को आवंटित करते समय इन बिंदुओं को ठीक से शुरू करने की तकनीकी समस्या को कैसे संभाल सकता हूं, शायद एक निर्दिष्ट संरचना जिसमें मनमानी गहराई तक संरचनाओं वाले एरे शामिल हैं?

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

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

इसके अलावा, बहुआयामी सरणियों के बारे में सोचें; आप प्रत्येक आयाम के लिए लंबाई मेटाडेटा को कहाँ संग्रहीत करेंगे जैसे कि आप अभी भी कुछ इस तरह से सरणी के माध्यम से चल सकते हैं

T *p = &a[0][0];

for ( size_t i = 0; i < rows; i++ )
  for ( size_t j = 0; j < cols; j++ )
    do_something_with( *p++ );

-2

प्रश्न मानता है कि सी। में सरणियाँ हैं। नहीं हैं। चीजें जिन्हें एरेज़ कहा जाता है, डेटा और पॉइंटर अंकगणित के निरंतर अनुक्रमों पर संचालन के लिए एक सिंटैक्टिक चीनी हैं।

निम्नलिखित कोड कुछ डेटा को src से dst में int-size chunks में कॉपी करता है ताकि यह पता न चले कि यह वास्तव में वर्ण स्ट्रिंग है।

char src[] = "Hello, world";
char dst[1024];
int *my_array = src; /* What? Compiler warning, but the code is valid. */
int *other_array = dst;
int i;
for (i = 0; i <= sizeof(src)/sizeof(int); i++)
    other_array[i] = my_array[i]; /* Oh well, we've copied some extra bytes */
printf("%s\n", dst);

क्यों C इतना सरलीकृत है कि इसमें उचित सरणियाँ नहीं हैं? मैं इस नए प्रश्न का सही उत्तर नहीं जानता। लेकिन कुछ लोग अक्सर कहते हैं कि सी सिर्फ (कुछ) अधिक पठनीय और पोर्टेबल असेंबलर है।


2
मुझे नहीं लगता कि आपने इस सवाल का जवाब दिया है।
रॉबर्ट हार्वे

2
आपने जो कहा वह सच है, लेकिन पूछने वाला जानना चाहता है कि ऐसा क्यों है।

9
याद रखें, सी के लिए उपनामों में से एक "पोर्टेबल असेंबली" है। हालांकि मानक के नए संस्करणों ने उच्च स्तर की अवधारणाओं को जोड़ा है, इसके मूल में, इसमें सरल निम्न स्तर के निर्माण और निर्देश शामिल हैं जो कि अधिकांश गैर-तुच्छ मशीनों में आम हैं। यह भाषा में किए गए अधिकांश डिज़ाइन निर्णयों को संचालित करता है। रनवे पर मौजूद एकमात्र चर पूर्णांक, फ्लोट्स और पॉइंटर्स हैं। निर्देशों में अंकगणित, तुलना और कूद शामिल हैं। बहुत ज्यादा सब कुछ एक पतली परत है कि ऊपर का निर्माण।

8
यह कहना गलत है कि सी में कोई सरणियां नहीं हैं, यह देखते हुए कि आप वास्तव में अन्य निर्माणों के साथ एक ही द्विआधारी कैसे उत्पन्न नहीं कर सकते हैं (ठीक है, कम से कम नहीं तो आप सरणी आकार का निर्धारण करने के लिए #defines के उपयोग पर विचार करते हैं)। सी में सरणी हैं "डेटा के निरंतर दृश्यों", इसके बारे में मीठा कुछ भी नहीं। बिंदुओं का उपयोग करना, जैसे कि वे सरणियाँ थीं, यहां वाक्यगत शर्करा है (स्पष्ट सूचक अंकगणित के बजाय), स्वयं को सरण नहीं।
हाइड

2
हां, इस कोड पर विचार करें struct Foo { int arr[10]; }:। arrएक सरणी है, एक सूचक नहीं है।
स्टीवन बर्नैप
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.