C सरणियों में 0 लंबाई क्यों नहीं हो सकती है?


13

C11 मानक कहता है कि सरणियाँ, दोनों का आकार और परिवर्तनशील लंबाई "शून्य से अधिक का मान होगा।" 0 की लंबाई की अनुमति नहीं देने का क्या औचित्य है?

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

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


7
stackoverflow.com/q/8625572 ... "शून्य लंबाई की एक सरणी मुश्किल होगी और इस आवश्यकता के साथ सामंजस्य करने के लिए भ्रामक होगा कि प्रत्येक वस्तु का एक अद्वितीय पता है।"
रॉबर्ट हार्वे

3
@ रोबर्टहाइवे: दिया गया struct { int p[1],q[1]; } foo; int *pp = p+1;, ppएक वैध सूचक *ppहोगा , लेकिन इसका कोई विशिष्ट पता नहीं होगा। एक ही तर्क शून्य-लंबाई सरणी के साथ क्यों नहीं रखा जा सकता है? यह कहें कि int q[0]; एक संरचना के भीतर दिया गया , qएक पते का उल्लेख करेगा जिसकी वैधता p+1ऊपर के उदाहरण की तरह होगी ।
सुपरकैट

@DocBrown C11 मानक से 6.7.6.2.5 एक वीएलए के आकार को निर्धारित करने के लिए उपयोग की जाने वाली अभिव्यक्ति के बारे में बात कर रहा है "... हर बार जब यह मूल्यांकन किया जाता है कि इसका मूल्य शून्य से अधिक होगा।" मैं C99 के बारे में नहीं जानता (और यह अजीब लगता है कि वे इसे बदल देंगे) लेकिन ऐसा लगता है कि आपके पास शून्य की लंबाई नहीं हो सकती है।
केविन कॉक्स

@ केविनकोक्स: क्या सी 11 मानक (या प्रश्न में हिस्सा) का मुफ्त ऑनलाइन संस्करण उपलब्ध है?
डॉक्टर ब्राउन

अंतिम संस्करण मुफ्त में उपलब्ध नहीं है (क्या शर्म की बात है) लेकिन आप ड्राफ्ट डाउनलोड कर सकते हैं। अंतिम उपलब्ध ड्राफ्ट open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf है
केविन कॉक्स

जवाबों:


11

इस मुद्दे पर मैं कहूंगा कि सी सरणियां स्मृति के आवंटित चंक की शुरुआत की ओर इशारा करती हैं। 0 आकार होने का मतलब होगा कि आपके पास एक संकेतक है ... कुछ भी नहीं? आपके पास कुछ भी नहीं हो सकता है, इसलिए कुछ मनमाना काम करना होगा। आप उपयोग नहीं कर सकते null, क्योंकि तब आपकी 0 लंबाई सरणियाँ अशक्त बिंदुओं की तरह दिखती होंगी। और उस बिंदु पर हर अलग कार्यान्वयन अलग-अलग मनमाना व्यवहार करने जा रहा है, जिससे अराजकता हो रही है।



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

3
@RobertHarvey सब सच है, लेकिन आपके समापन शब्द (और पूर्वव्यापी में पूरा जवाब) बस एक भ्रमित और भ्रमित करने वाला तरीका लगता है कि यह समझाने के लिए कि इस तरह की एक सरणी (मुझे लगता है कि इस जवाब को "स्मृति का आवंटित हिस्सा" कहा जाता है)? sizeof0, और कैसे परेशानी का कारण होगा। उन सभी को उचित अवधारणाओं और शब्दावली का उपयोग करके समझाया जा सकता है जिनमें संक्षिप्तता या स्पष्टता का कोई नुकसान नहीं है। सरणियों और बिंदुओं को मिलाने से केवल लाभ के लिए सरणियों = बिंदु गलत धारणा (जो अन्य संदर्भों में अधिक महत्वपूर्ण है) को फैलाने का जोखिम होता है।

2
" आप अशक्त का उपयोग नहीं कर सकते, क्योंकि तब आपकी 0 लंबाई सरणियाँ अशक्त बिंदुओं की तरह दिखेंगी " - वास्तव में यही डेल्फी करता है। खाली वंश और खाली दीर्घवृत्त तकनीकी रूप से अशक्त बिंदु हैं।
जेन्स जीएल

3
-1, मैं यहां @delnan से भरा हुआ हूं। यह कुछ भी नहीं बताता है, विशेष रूप से ओपी ने शून्य लंबाई सरणियों की अवधारणा का समर्थन करने वाले कुछ प्रमुख संकलकों के बारे में क्या लिखा है, इस संदर्भ में। मुझे पूरा यकीन है कि एक कार्यान्वयन-अयोग्य तरीके से शून्य लंबाई सरणियों को सी में प्रदान किया जा सकता है, न कि "अराजकता के लिए अग्रणी"।
डॉक्टर ब्राउन

6

आइए देखें कि स्मृति में आमतौर पर एक सरणी कैसे रखी जाती है:

         +----+
arr[0] : |    |
         +----+
arr[1] : |    |
         +----+
arr[2] : |    |
         +----+
          ...
         +----+
arr[n] : |    |
         +----+

ध्यान दें कि एक अलग ऑब्जेक्ट का नाम नहीं है arrजो पहले तत्व का पता संग्रहीत करता है; जब कोई एक्सप्रेशन किसी एक्सप्रेशन में दिखाई देता है, तो C पहले एलिमेंट के एड्रेस की जरूरत के अनुसार गणना करता है।

तो, चलो इस बारे में सोचें: 0-तत्व सरणी के लिए होता है कोई भंडारण इसके लिए अलग सेट, सरणी पते गणना करने के लिए कुछ भी नहीं है जिसका अर्थ से (दूसरे शब्दों में कहें, वहाँ पहचानकर्ता के लिए कोई वस्तु मानचित्रण है)। यह कहने जैसा है, "मैं एक ऐसा intचर बनाना चाहता हूं जिसमें कोई स्मृति न हो।" यह एक निरर्थक ऑपरेशन है।

संपादित करें

जावा सरणियां C और C ++ सरणियों से पूरी तरह से अलग जानवर हैं; वे एक आदिम प्रकार नहीं हैं, लेकिन एक संदर्भ प्रकार से लिया गया है Object

संपादित करें

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

यह स्पष्ट है कि वीएलए नियमित सरणियों से अलग जानवर हैं , और उनके कार्यान्वयन 0 आकार के लिए अनुमति दे सकते हैं । उन्हें घोषित staticया फ़ाइल स्कोप पर नहीं किया जा सकता , क्योंकि प्रोग्राम शुरू होने से पहले ऐसी वस्तुओं का आकार ज्ञात होना चाहिए।

यह भी ध्यान देने योग्य है कि C11 के रूप में, कार्यान्वयन को वीएलएएस का समर्थन करने की आवश्यकता नहीं है।


3
क्षमा करें, लेकिन IMHO आपको बिंदु याद आ रहा है, जैसे टेलस्टीन। शून्य लंबाई सरणियाँ बहुत मायने रखती हैं, और मौजूदा कार्यान्वयन जैसे ओपी ने हमें शो के बारे में बताया कि यह किया जा सकता है।
डॉक्टर ब्राउन

@DocBrown: सबसे पहले, मैं संबोधित कर रहा था कि भाषा मानक सबसे अधिक संभावना उन्हें क्यों नहीं रोकता है। दूसरे, मैं एक उदाहरण चाहूंगा जहां 0-लंबाई की सरणी समझ में आती है, क्योंकि मैं ईमानदारी से एक के बारे में नहीं सोच सकता। सबसे अधिक संभावना कार्यान्वयन के T a[0]रूप में इलाज करना है T *a, लेकिन फिर सिर्फ उपयोग क्यों नहीं T *a?
जॉन बोडे

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

@DocBrown: आह। structहैक। मैंने इसे व्यक्तिगत रूप से उपयोग नहीं किया है; उस समस्या पर कभी काम नहीं किया जिसके लिए एक structप्रकार के आकार की आवश्यकता होती है ।
जॉन बोडे

2
और सी 99 के बाद से AFAIK को नहीं भूलना, सी चर-लंबाई सरणियों की अनुमति देता है। और जब सरणी का आकार एक पैरामीटर होता है, तो 0 के मान का इलाज न करना एक विशेष मामले के रूप में बहुत सारे कार्यक्रमों को सरल बना सकता है।
डॉक ब्राउन

2

आप आमतौर पर अपने शून्य (वास्तव में परिवर्तनशील) आकार सरणी को चलाने के समय में इसका आकार जानना चाहेंगे। फिर उसे पैक structकरें और लचीले सरणी सदस्यों का उपयोग करें , जैसे:

struct my_st {
   unsigned len;
   double flexarray[]; // of size len
};

जाहिर है कि लचीली सरणी सदस्य को अपने में अंतिम होना चाहिए structऔर आपको पहले कुछ होना चाहिए। अक्सर यह उस लचीले सरणी सदस्य के वास्तविक रन-टाइम की लंबाई से संबंधित कुछ होगा।

बेशक आप आवंटित करेंगे:

 unsigned len = some_length_computation();
 struct my_st*p = malloc(sizeof(struct my_st)+len*sizeof(double));
 if (!p) { perror("malloc my_st"); exit(EXIT_FAILURE); };
 p->len = len;
 for (unsigned ix=0; ix<len; ix++)
    p->flexarray[ix] = log(3.0+(double)ix);

AFAIK, यह पहले से ही C99 में संभव था, और यह बहुत उपयोगी है।

BTW, लचीली सरणी सदस्य C ++ में मौजूद नहीं हैं (क्योंकि यह परिभाषित करना मुश्किल होगा कि उन्हें कब और कैसे निर्माण और नष्ट किया जाना चाहिए)। हालाँकि भविष्य के std :: dynarray देखें


तुम्हें पता है, वे सिर्फ तुच्छ प्रकारों के लिए विवश हो सकते हैं, और कोई कठिनाई नहीं होगी।
डेडुप्लिकेटर

2

यदि अभिव्यक्ति type name[count]किसी फ़ंक्शन में लिखी गई है, तो आप सी कंपाइलर को स्टैक फ्रेम sizeof(type)*countबाइट्स पर आवंटित करने और सरणी में पहले तत्व के पते की गणना करने के लिए कहते हैं।

यदि अभिव्यक्ति type name[count]सभी कार्यों और संरचना परिभाषाओं के बाहर लिखी गई है, तो आप सी कंपाइलर को डेटा खंड sizeof(type)*countबाइट्स पर आवंटित करने और सरणी में पहले तत्व के पते की गणना करने के लिए कहते हैं।

nameवास्तव में निरंतर ऑब्जेक्ट है जो सरणी में पहले तत्व के पते को संग्रहीत करता है और प्रत्येक ऑब्जेक्ट जो किसी मेमोरी के पते को संग्रहीत करता है, उसे पॉइंटर कहा जाता है, इसलिए यही कारण है कि आप nameएक सरणी के बजाय एक पॉइंटर के रूप में व्यवहार करते हैं । ध्यान दें कि सी में सरणियों को केवल पॉइंटर्स के माध्यम से एक्सेस किया जा सकता है।

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

यह तर्कसंगत है कि तत्व नं। -length सरणी count+1में मौजूद नहीं है count, तो यही कारण है कि सी कंपाइलर शून्य-लंबाई सरणी को एक फ़ंक्शन के अंदर और बाहर चर के रूप में परिभाषित करने से मना करता है, क्योंकि nameतब की सामग्री क्या है ? क्या पते nameवास्तव में संग्रहीत करता है?

यदि pएक सूचक है तो अभिव्यक्ति p[n]के बराबर है*(p + n)

जहाँ सही अभिव्यक्ति में तारांकन * पॉइंटर का डीपरेशन ऑपरेशन होता है, जिसका अर्थ है कि पॉइंट द्वारा मेमोरी को एक्सेस करना p + nया उस एक्सेस को एक्सेस करना जिसके एड्रेस में स्टोर किया गया है p + n, जहां p + nपॉइंटर एक्सप्रेशन है, यह एड्रेस को लेता है pऔर इस एड्रेस पर नंबर को nगुणा करता है पॉइंटर के प्रकार का आकार p

क्या पता और नंबर जोड़ना संभव है?

हाँ यह संभव है, क्योंकि पता अहस्ताक्षरित पूर्णांक है जिसे सामान्यतः हेक्साडेसिमल नोटेशन में दर्शाया गया है।


कई कंपाइलरों ने मानक आकार से पहले शून्य-आकार की सरणी घोषणाओं की अनुमति देने के लिए इसका इस्तेमाल किया, और कई ने विस्तार के रूप में ऐसी घोषणाओं को जारी रखने की अनुमति दी। इस तरह की घोषणाओं अगर एक मान्यता है कि आकार की एक वस्तु कोई समस्या का कारण होगा Nहै N+1पते, पहले जुड़े Nजिनमें से अद्वितीय बाइट्स और आखिरी पहचान Nजो प्रत्येक बिंदु उन बाइट्स की सिर्फ पिछले एक की। इस तरह की परिभाषा पतित मामले में भी ठीक काम करेगी, जहां N0.
सुपरकैट

1

यदि आप एक मेमोरी पते के लिए एक पॉइंटर चाहते हैं, तो एक को घोषित करें। एक सरणी वास्तव में आपके द्वारा आरक्षित मेमोरी के एक हिस्से पर इंगित करती है। कार्यों के लिए जाने पर संकेत करने के लिए क्षय को रोकता है, लेकिन अगर वे जिस स्मृति को इंगित कर रहे हैं वह ढेर पर है, कोई समस्या नहीं है। आकार शून्य की एक सरणी घोषित करने का कोई कारण नहीं है।


2
आम तौर पर आप इसे सीधे नहीं करते हैं, लेकिन एक मैक्रो के परिणामस्वरूप या जब गतिशील डेटा के साथ एक चर लंबाई सरणी की घोषणा करते हैं।
केविन कॉक्स

एक सरणी कभी नहीं इंगित करता है। इसमें पॉइंटर्स हो सकते हैं, और अधिकांश संदर्भों में आप वास्तव में पहले तत्व के लिए एक पॉइंटर का उपयोग करते हैं, लेकिन यह एक अलग कहानी है।
Deduplicator

1
सरणी का नाम सरणी में निहित मेमोरी के लिए एक निरंतर सूचक है।
ncmathsadist

1
नहीं है, सरणी नाम करने के लिए decays अधिकांश संदर्भों में पहला तत्व के लिए सूचक,। अंतर अक्सर महत्वपूर्ण होता है।
डिडुप्लिकेटर

1

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

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.