मैं C ++ में सरणियों का उपयोग कैसे करूं?


480

C ++ को C से विरासत में मिली एरियर्स जहाँ वे वस्तुतः हर जगह उपयोग की जाती हैं। C ++ ऐसे सार प्रदान करता है जो उपयोग करने में आसान और कम त्रुटि-प्रवण हैं ( std::vector<T>C ++ 98 और C ++ 11 केstd::array<T, n> बाद से ), इसलिए सरणियों की आवश्यकता उतनी बार नहीं उठती जितनी बार सी में होती है। हालांकि, जब आप विरासत पढ़ते हैं कोड या C में लिखी गई लाइब्रेरी के साथ इंटरेक्ट करना, आपके पास एरे पर काम करने का तरीका होना चाहिए।

इस FAQ को पाँच भागों में विभाजित किया गया है:

  1. प्रकार के स्तर और पहुंच वाले तत्वों पर सरणियाँ
  2. सरणी निर्माण और आरंभीकरण
  3. असाइनमेंट और पैरामीटर पासिंग
  4. बहुआयामी सरणियों और संकेत के सरणियों
  5. सरणियों का उपयोग करते समय आम नुकसान

यदि आपको लगता है कि इस एफएक्यू में कुछ महत्वपूर्ण छूट गया है, तो एक उत्तर लिखें और इसे एक अतिरिक्त भाग के रूप में यहां लिंक करें।

निम्नलिखित पाठ में, "सरणी" का अर्थ "सी सरणी" है, वर्ग टेम्पलेट नहीं std::array। सी घोषणाकर्ता सिंटैक्स का बुनियादी ज्ञान ग्रहण किया जाता है। ध्यान दें कि के मैनुअल उपयोग newऔर deleteजैसा कि नीचे बताया अपवाद का सामना करने में बेहद खतरनाक है, लेकिन इस बात का विषय है एक और पूछे जाने वाले प्रश्न

(नोट: यह स्टैक ओवरफ्लो के C ++ FAQ के लिए प्रविष्टि माना जाता है । यदि आप इस फॉर्म में FAQ प्रदान करने के विचार की आलोचना करना चाहते हैं, तो मेटा पर पोस्टिंग जो यह सब शुरू करती है वह करने के लिए जगह होगी। उत्तर) उस प्रश्न की निगरानी C ++ चैटरूम में की जाती है , जहाँ पहली बार में FAQ विचार शुरू हुआ है, इसलिए आपका उत्तर उन लोगों द्वारा पढ़ा जाने की संभावना है, जो विचार के साथ आए थे।)


वे और भी बेहतर होते अगर पॉइंटर्स हमेशा अपने लक्ष्य के बीच में कहीं और भीख मांगने की ओर इशारा करते, हालाँकि ...
Deduplicator

आपको एसटीएल वेक्टर का उपयोग करना चाहिए क्योंकि यह आपको अधिक लचीलापन प्रदान करता है।
मोजिज साजिद

2
std::arrayS, std::vectors और gsl::spans की संयुक्त उपलब्धता के साथ - मैं स्पष्ट रूप से C ++ में सरणियों का उपयोग करने के तरीके के बारे में अक्सर पूछे जाने वाले प्रश्न की अपेक्षा करता हूं कि "अब तक, आप उनका उपयोग करते हुए , बस, अच्छी तरह से विचार करना शुरू कर सकते हैं।"
einpoklum

जवाबों:


302

प्रकार के स्तर पर पहुंचता है

एक सरणी प्रकार को इस प्रकार दर्शाया जाता है कि तत्व प्रकारT[n] कहां Tहै और एक सकारात्मक हैn आकार , सरणी में तत्वों की संख्या। सरणी प्रकार तत्व प्रकार और आकार का एक उत्पाद प्रकार है। यदि उन सामग्रियों में से एक या दोनों अलग हैं, तो आपको एक अलग प्रकार मिलता है:

#include <type_traits>

static_assert(!std::is_same<int[8], float[8]>::value, "distinct element type");
static_assert(!std::is_same<int[8],   int[9]>::value, "distinct size");

ध्यान दें कि आकार प्रकार का हिस्सा है, अर्थात, विभिन्न प्रकार के सरणी प्रकार असंगत प्रकार हैं जिनका एक दूसरे से कोई लेना-देना नहीं है। sizeof(T[n])के बराबर है n * sizeof(T)

एरे-टू-पॉइंटर क्षय

केवल "कनेक्शन" के बीच है T[n]और T[m]यह है कि दोनों प्रकारों को संक्षेप में परिवर्तित किया जा सकता है T*, और इस रूपांतरण का परिणाम सरणी के पहले तत्व के लिए एक संकेतक है। अर्थात्, कहीं भी T*आवश्यकता है, आप एक प्रदान कर सकते हैं T[n], और संकलक चुपचाप उस सूचक को प्रदान करेगा:

                  +---+---+---+---+---+---+---+---+
the_actual_array: |   |   |   |   |   |   |   |   |   int[8]
                  +---+---+---+---+---+---+---+---+
                    ^
                    |
                    |
                    |
                    |  pointer_to_the_first_element   int*

इस रूपांतरण को "सरणी-से-पॉइंटर क्षय" के रूप में जाना जाता है, और यह भ्रम का एक प्रमुख स्रोत है। इस प्रक्रिया में सरणी का आकार खो गया है, क्योंकि यह अब प्रकार ( T*) का हिस्सा नहीं है । प्रो: प्रकार के स्तर पर किसी सरणी के आकार को भूल जाने से सूचक किसी भी आकार के सरणी के पहले तत्व को इंगित कर सकता है । Con: सरणी के पहले (या किसी अन्य) तत्व के लिए एक पॉइंटर को देखते हुए, यह पता लगाने का कोई तरीका नहीं है कि वह सरणी कितनी बड़ी है या जहां पॉइंटर सरणी की सीमा के सापेक्ष इंगित करता है। संकेत अत्यंत मूर्ख हैं

ऐरे पॉइंट्स नहीं हैं

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

static_assert(!std::is_same<int[8], int*>::value, "an array is not a pointer");

एक महत्वपूर्ण संदर्भ जिसमें एक सरणी अपने पहले तत्व के लिए एक सूचक में क्षय नहीं करता है जब &ऑपरेटर उस पर लागू होता है। उस स्थिति में, &ऑपरेटर अपने पहले तत्व के लिए केवल एक सूचक नहीं, बल्कि पूरे सरणी के लिए एक संकेतक देता है। हालाँकि उस स्थिति में मान (पते) समान होते हैं, एक सरणी के पहले तत्व के लिए एक सूचक और पूरे सरणी के लिए एक सूचक पूरी तरह से अलग प्रकार होते हैं:

static_assert(!std::is_same<int*, int(*)[8]>::value, "distinct element type");

निम्नलिखित ASCII कला इस भेद की व्याख्या करती है:

      +-----------------------------------+
      | +---+---+---+---+---+---+---+---+ |
+---> | |   |   |   |   |   |   |   |   | | int[8]
|     | +---+---+---+---+---+---+---+---+ |
|     +---^-------------------------------+
|         |
|         |
|         |
|         |  pointer_to_the_first_element   int*
|
|  pointer_to_the_entire_array              int(*)[8]

ध्यान दें कि पहला तत्व कैसे सूचक केवल एक पूर्णांक (एक छोटे से बॉक्स के रूप में दर्शाया गया) को इंगित करता है, जबकि पूरे सरणी के लिए सूचक 8 पूर्णांक (एक बड़े बॉक्स के रूप में दर्शाया गया) की एक सरणी को इंगित करता है।

कक्षाओं में एक ही स्थिति पैदा होती है और शायद अधिक स्पष्ट है। किसी ऑब्जेक्ट का पॉइंटर और उसके पहले डेटा मेंबर के लिए पॉइंटर का मान समान होता है (एक ही पता) होता है, फिर भी वे पूरी तरह से अलग प्रकार के होते हैं।

यदि आप C घोषणाकर्ता वाक्यविन्यास से अपरिचित हैं, तो टाइप में कोष्ठक int(*)[8]आवश्यक हैं:

  • int(*)[8] 8 पूर्णांकों की एक सरणी के लिए एक संकेतक है।
  • int*[8]8 पॉइंटर्स की एक सरणी है, प्रत्येक प्रकार का तत्व int*

तत्वों तक पहुँचना

C ++ एक सरणी के अलग-अलग तत्वों तक पहुंचने के लिए दो सिंटैक्टिक विविधताएं प्रदान करता है। उनमें से कोई भी दूसरे से बेहतर नहीं है, और आपको अपने आप को दोनों के साथ परिचित करना चाहिए।

सूचक अंकगणित

pकिसी सरणी के पहले तत्व के लिए एक पॉइंटर को देखते हुए , अभिव्यक्ति p+iपॉइंटर को i-th तत्व के लिए देता है। बाद में उस सूचक को dereferencing करके, एक व्यक्ति तत्वों तक पहुंच सकता है:

std::cout << *(x+3) << ", " << *(x+7) << std::endl;

यदि xकिसी ऐरे को निरूपित करते हैं , तो एरे-टू-पॉइंटर क्षय किक करेगा, क्योंकि एरे और पूर्णांक जोड़ना अर्थहीन है (सरणियों पर कोई प्लस ऑपरेशन नहीं है), लेकिन एक पॉइंटर और पूर्णांक जोड़ने से समझ में आता है:

   +---+---+---+---+---+---+---+---+
x: |   |   |   |   |   |   |   |   |   int[8]
   +---+---+---+---+---+---+---+---+
     ^           ^               ^
     |           |               |
     |           |               |
     |           |               |
x+0  |      x+3  |          x+7  |     int*

(ध्यान दें कि अंतर्निहित उत्पन्न सूचक का कोई नाम नहीं है, इसलिए मैंने x+0इसे पहचानने के लिए लिखा था।)

यदि, दूसरी ओर, xकिसी पॉइंटर को पहले (या किसी अन्य) एलीमेंट के तत्व को दर्शाता है , तो एरे-टू-पॉइंटर क्षय आवश्यक नहीं है, क्योंकि जिस पॉइंटर को iजोड़ा जाना है, वह पहले से मौजूद है:

   +---+---+---+---+---+---+---+---+
   |   |   |   |   |   |   |   |   |   int[8]
   +---+---+---+---+---+---+---+---+
     ^           ^               ^
     |           |               |
     |           |               |
   +-|-+         |               |
x: | | |    x+3  |          x+7  |     int*
   +---+

ध्यान दें कि दर्शाए गए मामले में, xएक पॉइंटर वैरिएबल है (छोटे बॉक्स के बगल में स्थित है x), लेकिन यह एक पॉइंटर (या किसी अन्य प्रकार की अभिव्यक्ति) को वापस करने वाले फ़ंक्शन का परिणाम हो सकता हैT* )।

अनुक्रमणिका संचालक

चूंकि सिंटैक्स *(x+i)थोड़ा अनाड़ी है, C ++ वैकल्पिक सिंटैक्स प्रदान करता है x[i]:

std::cout << x[3] << ", " << x[7] << std::endl;

इस तथ्य के कारण कि अतिरिक्त सराहनीय है, निम्नलिखित कोड बिल्कुल वैसा ही करता है:

std::cout << 3[x] << ", " << 7[x] << std::endl;

इंडेक्सिंग ऑपरेटर की परिभाषा निम्नलिखित दिलचस्प समानता की ओर ले जाती है:

&x[i]  ==  &*(x+i)  ==  x+i

हालांकि, &x[0]आम तौर पर इसके समकक्ष नहीं है x। पूर्व एक सूचक है, बाद वाला एक सरणी है। केवल जब संदर्भ एरे-टू-पॉइंटर क्षय को ट्रिगर करता है xऔर &x[0]परस्पर विनिमय किया जा सकता है। उदाहरण के लिए:

T* p = &array[0];  // rewritten as &*(array+0), decay happens due to the addition
T* q = array;      // decay happens due to the assignment

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

सीमाओं

प्रकार की एक सरणी T[n]है nतत्वों, से अनुक्रमित 0करने के लिए n-1; कोई तत्व नहीं है n। और फिर भी, आधी-खुली सीमाओं (जहां शुरुआत समावेशी है और अंत अनन्य है ) का समर्थन करने के लिए, C ++ पॉइंटर को (गैर-मौजूद) एन-वें तत्व की गणना करने की अनुमति देता है, लेकिन यह पॉइरेन्सेशन के लिए अवैध है:

   +---+---+---+---+---+---+---+---+....
x: |   |   |   |   |   |   |   |   |   .   int[8]
   +---+---+---+---+---+---+---+---+....
     ^                               ^
     |                               |
     |                               |
     |                               |
x+0  |                          x+8  |     int*

उदाहरण के लिए, यदि आप किसी सरणी को क्रमबद्ध करना चाहते हैं, तो निम्नलिखित दोनों समान रूप से अच्छी तरह से काम करेंगे:

std::sort(x + 0, x + n);
std::sort(&x[0], &x[0] + n);

ध्यान दें कि यह &x[n]दूसरे तर्क के रूप में प्रदान करना अवैध है क्योंकि यह इसके बराबर है &*(x+n), और उप-अभिव्यक्ति *(x+n)तकनीकी रूप से अपरिभाषित व्यवहार को आमंत्रित करती है से C ++ (लेकिन C99 में नहीं) में को ।

यह भी ध्यान दें कि आप xपहले तर्क के रूप में प्रदान कर सकते हैं । यह मेरे स्वाद के लिए थोड़ा बहुत कठिन है, और यह संकलक के लिए टेम्प्लेट तर्क को थोड़ा कठिन बना देता है, क्योंकि उस मामले में पहला तर्क एक सरणी है लेकिन दूसरा तर्क एक सूचक है। (फिर से, सरणी-से-पॉइंटर क्षय में किक करता है।)


ऐसे मामले जहां सरणी सूचक में क्षय नहीं करती है , संदर्भ के लिए यहां चित्रित किया गया है
लेजेंड्स 2

@fredoverflow पहुँच या रेंज में भाग यह उल्लेख के लायक हो सकता है कि C- सरणियाँ लूप के लिए C ++ 11 रेंज-आधारित के साथ काम करती हैं।
gnzlbg

135

प्रोग्रामर अक्सर बिंदुओं के सरणियों के साथ बहुआयामी सरणियों को भ्रमित करते हैं।

बहुआयामी सरणियाँ

अधिकांश प्रोग्रामर नामित बहुआयामी सरणियों से परिचित हैं, लेकिन कई इस तथ्य से अनजान हैं कि बहुआयामी सरणी को गुमनाम रूप से भी बनाया जा सकता है। बहुआयामी सरणियों को अक्सर "सरणियों के सारणी" या " सच्चे बहुआयामी सरणियों" के रूप में जाना जाता है ।

जिसे बहुआयामी सरणियों का नाम दिया गया है

नामित बहुआयामी सरणियों का उपयोग करते समय, सभी आयामों को संकलन समय पर जाना चाहिए:

int H = read_int();
int W = read_int();

int connect_four[6][7];   // okay

int connect_four[H][7];   // ISO C++ forbids variable length array
int connect_four[6][W];   // ISO C++ forbids variable length array
int connect_four[H][W];   // ISO C++ forbids variable length array

यह एक नामित बहुआयामी सरणी स्मृति में कैसा दिखता है:

              +---+---+---+---+---+---+---+
connect_four: |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+

ध्यान दें कि उपर्युक्त जैसे 2D ग्रिड केवल उपयोगी दृश्य हैं। C ++ के दृष्टिकोण से, मेमोरी बाइट्स का एक "फ्लैट" अनुक्रम है। एक बहुआयामी सरणी के तत्वों को पंक्ति-प्रमुख क्रम में संग्रहीत किया जाता है। यही है, connect_four[0][6]और connect_four[1][0]स्मृति में पड़ोसी हैं। वास्तव में, connect_four[0][7]और connect_four[1][0]एक ही तत्व को निरूपित करते हैं! इसका मतलब है कि आप बहुआयामी सरणियों को ले सकते हैं और उन्हें बड़े, एक आयामी सरणियों के रूप में मान सकते हैं:

int* p = &connect_four[0][0];
int* q = p + 42;
some_int_sequence_algorithm(p, q);

अनाम बहुआयामी सरणियाँ

अनाम बहुआयामी सरणियों के साथ, पहले को छोड़कर सभी आयामों को संकलन समय पर जाना चाहिए:

int (*p)[7] = new int[6][7];   // okay
int (*p)[7] = new int[H][7];   // okay

int (*p)[W] = new int[6][W];   // ISO C++ forbids variable length array
int (*p)[W] = new int[H][W];   // ISO C++ forbids variable length array

यह कैसे एक अनाम बहुआयामी सरणी स्मृति में दिखता है:

              +---+---+---+---+---+---+---+
        +---> |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |
      +-|-+
   p: | | |
      +---+

ध्यान दें कि सरणी अभी भी स्मृति में एकल ब्लॉक के रूप में आवंटित की गई है।

बिंदुओं की सरणियाँ

आप दूसरे स्तर के अप्रत्यक्ष रूप को शुरू करके निश्चित चौड़ाई के प्रतिबंध को दूर कर सकते हैं।

बिंदुओं के नामांकित सरणियाँ

यहां पांच बिंदुओं का नाम दिया गया है, जो विभिन्न लंबाई के अनाम सरणियों के साथ आरंभ किए गए हैं:

int* triangle[5];
for (int i = 0; i < 5; ++i)
{
    triangle[i] = new int[5 - i];
}

// ...

for (int i = 0; i < 5; ++i)
{
    delete[] triangle[i];
}

और यहाँ है कि यह स्मृति में कैसा दिखता है:

          +---+---+---+---+---+
          |   |   |   |   |   |
          +---+---+---+---+---+
            ^
            | +---+---+---+---+
            | |   |   |   |   |
            | +---+---+---+---+
            |   ^
            |   | +---+---+---+
            |   | |   |   |   |
            |   | +---+---+---+
            |   |   ^
            |   |   | +---+---+
            |   |   | |   |   |
            |   |   | +---+---+
            |   |   |   ^
            |   |   |   | +---+
            |   |   |   | |   |
            |   |   |   | +---+
            |   |   |   |   ^
            |   |   |   |   |
            |   |   |   |   |
          +-|-+-|-+-|-+-|-+-|-+
triangle: | | | | | | | | | | |
          +---+---+---+---+---+

चूंकि प्रत्येक पंक्ति को अब व्यक्तिगत रूप से आवंटित किया गया है, 1 डी सरणियों के रूप में 2 डी सरणियों को देखना अब काम नहीं करता है।

अनाम बिंदुओं की बेनामी सरणियाँ

यहां 5 (या किसी भी अन्य) पॉइंटर्स का एक अनाम सरणी है जो विभिन्न लंबाई के अनाम सरणियों के साथ आरंभ किया गया है:

int n = calculate_five();   // or any other number
int** p = new int*[n];
for (int i = 0; i < n; ++i)
{
    p[i] = new int[n - i];
}

// ...

for (int i = 0; i < n; ++i)
{
    delete[] p[i];
}
delete[] p;   // note the extra delete[] !

और यहाँ है कि यह स्मृति में कैसा दिखता है:

          +---+---+---+---+---+
          |   |   |   |   |   |
          +---+---+---+---+---+
            ^
            | +---+---+---+---+
            | |   |   |   |   |
            | +---+---+---+---+
            |   ^
            |   | +---+---+---+
            |   | |   |   |   |
            |   | +---+---+---+
            |   |   ^
            |   |   | +---+---+
            |   |   | |   |   |
            |   |   | +---+---+
            |   |   |   ^
            |   |   |   | +---+
            |   |   |   | |   |
            |   |   |   | +---+
            |   |   |   |   ^
            |   |   |   |   |
            |   |   |   |   |
          +-|-+-|-+-|-+-|-+-|-+
          | | | | | | | | | | |
          +---+---+---+---+---+
            ^
            |
            |
          +-|-+
       p: | | |
          +---+

रूपांतरण

एरे-टू-पॉइंटर क्षय स्वाभाविक रूप से एरे और एरे के बिंदुओं के सरणियों तक फैलता है:

int array_of_arrays[6][7];
int (*pointer_to_array)[7] = array_of_arrays;

int* array_of_pointers[6];
int** pointer_to_pointer = array_of_pointers;

हालांकि, से कोई अंतर्निहित रूपांतरण नहीं T[h][w]है T**। अगर इस तरह के एक अंतर्निहित रूपांतरण अस्तित्व था, परिणाम की एक सरणी के पहले तत्व के लिए सूचक होगा hकी ओर इशारा T(मूल 2 डी सरणी में एक पंक्ति के पहले तत्व के लिए प्रत्येक इशारा), लेकिन यह सूचक सरणी में कहीं भी मौजूद नहीं है स्मृति अभी तक। यदि आप ऐसा रूपांतरण चाहते हैं, तो आपको आवश्यक पॉइंटर सरणी को मैन्युअल रूप से बनाना और भरना होगा:

int connect_four[6][7];

int** p = new int*[6];
for (int i = 0; i < 6; ++i)
{
    p[i] = connect_four[i];
}

// ...

delete[] p;

ध्यान दें कि यह मूल बहुआयामी सरणी का एक दृश्य उत्पन्न करता है। यदि आपको इसके बजाय प्रतिलिपि की आवश्यकता है, तो आपको अतिरिक्त सरणियाँ बनानी होंगी और डेटा को स्वयं कॉपी करना होगा:

int connect_four[6][7];

int** p = new int*[6];
for (int i = 0; i < 6; ++i)
{
    p[i] = new int[7];
    std::copy(connect_four[i], connect_four[i + 1], p[i]);
}

// ...

for (int i = 0; i < 6; ++i)
{
    delete[] p[i];
}
delete[] p;

सुझाव के रूप में: आप का कहना है, कि चाहिए int connect_four[H][7];, int connect_four[6][W]; int connect_four[H][W];के साथ-साथ int (*p)[W] = new int[6][W];और int (*p)[W] = new int[H][W];वैध बयान, जब कर रहे हैं Hऔर Wसंकलन समय पर जाना जाता है।
राबर्ट्स

88

असाइनमेंट

बिना किसी विशेष कारण के, सरणियों को एक दूसरे को नहीं सौंपा जा सकता है। std::copyइसके बजाय उपयोग करें :

#include <algorithm>

// ...

int a[8] = {2, 3, 5, 7, 11, 13, 17, 19};
int b[8];
std::copy(a + 0, a + 8, b);

यह सच सरणी असाइनमेंट प्रदान कर सकता है की तुलना में अधिक लचीला है क्योंकि बड़े सरणियों के स्लाइस को छोटे सरणियों में कॉपी करना संभव है। std::copyआम तौर पर अधिकतम प्रदर्शन देने के लिए आदिम प्रकारों के लिए विशेष है। यह संभावना नहीं है किstd::memcpy । यदि संदेह है, तो मापें।

यद्यपि आप सीधे सरणियाँ नहीं दे सकते हैं , लेकिन आप उन संरचनों और वर्गों को असाइन कर सकते हैं जिनमें सरणी सदस्य होते हैं। ऐसा इसलिए है क्योंकि सरणी सदस्यों को सदस्यवार कॉपी किया जाता है असाइनमेंट ऑपरेटर द्वारा की जाती है जो संकलक द्वारा डिफ़ॉल्ट के रूप में प्रदान की जाती है। यदि आप असाइनमेंट ऑपरेटर को मैन्युअल रूप से अपनी संरचना या वर्ग प्रकारों के लिए परिभाषित करते हैं, तो आपको सरणी सदस्यों के लिए मैन्युअल कॉपी पर वापस आना होगा।

पैरामीटर पासिंग

एरे को मान से पास नहीं किया जा सकता है। आप उन्हें पॉइंटर से या संदर्भ से पास कर सकते हैं।

सूचक द्वारा पास

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

#include <numeric>
#include <cstddef>

int sum(const int* p, std::size_t n)
{
    return std::accumulate(p, p + n, 0);
}

int sum(const int* p, const int* q)
{
    return std::accumulate(p, q, 0);
}

एक सिंटैक्टिक विकल्प के रूप में, आप पैरामीटर को भी घोषित कर सकते हैं T p[], और इसका मतलब वही है T* p जो पैरामीटर सूचियों के संदर्भ में है :

int sum(const int p[], std::size_t n)
{
    return std::accumulate(p, p + n, 0);
}

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

दुर्भाग्य से, आप एक सरणी पैरामीटर में एक आकार भी प्रदान कर सकते हैं जिसे कंपाइलर द्वारा चुपचाप अनदेखा कर दिया जाता है। अर्थात, निम्नलिखित तीन हस्ताक्षर बिल्कुल समतुल्य हैं, जैसा कि संकलक त्रुटियों से संकेत मिलता है:

int sum(const int* p, std::size_t n)

// error: redefinition of 'int sum(const int*, size_t)'
int sum(const int p[], std::size_t n)

// error: redefinition of 'int sum(const int*, size_t)'
int sum(const int p[8], std::size_t n)   // the 8 has no meaning here

संदर्भ से गुजारें

एरे को संदर्भ द्वारा भी पास किया जा सकता है:

int sum(const int (&a)[8])
{
    return std::accumulate(a + 0, a + 8, 0);
}

इस मामले में, सरणी का आकार महत्वपूर्ण है। एक फ़ंक्शन लिखने के बाद से जो केवल 8 तत्वों के सरणियों को स्वीकार करता है, बहुत कम उपयोग होता है, प्रोग्रामर आमतौर पर ऐसे कार्यों को टेम्पलेट के रूप में लिखते हैं:

template <std::size_t n>
int sum(const int (&a)[n])
{
    return std::accumulate(a + 0, a + n, 0);
}

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


2
यह एक नोट भी है कि में यद्यपि जोड़ने के लिए लायक हो सकता है void foo(int a[3]) aकी तरह एक मूल्य द्वारा सरणी से गुजर रहा है, को संशोधित नज़र करता है aके अंदर fooमूल सरणी को संशोधित करेगा। यह स्पष्ट होना चाहिए क्योंकि सरणियों की प्रतिलिपि नहीं बनाई जा सकती है, लेकिन इसे सुदृढ़ करने के लिए इसके लायक हो सकता है।
gnzlbg

C ++ 20 में हैranges::copy(a, b)
LF

int sum( int size_, int a[size_]);- (मुझे लगता है) C99 आगे से
शेफ ग्लेडिएटर

73

5. सरणियों का उपयोग करते समय सामान्य नुकसान।

5.1 ख़तरा: टाइपिंग-असुरक्षित लिंकिंग पर भरोसा करना।

ठीक है, आपको बताया गया है, या अपने आप को पता चला है, कि ग्लोबल्स (नामस्थान गुंजाइश चर जो अनुवाद इकाई के बाहर तक पहुँचा जा सकता है) ईविल ™ हैं। लेकिन क्या आप जानते हैं कि वास्तव में ईविल ™ वे कैसे हैं? नीचे दिए गए कार्यक्रम पर विचार करें, जिसमें दो फाइलें शामिल हैं [main.cpp] और [numbers.cpp]:

// [main.cpp]
#include <iostream>

extern int* numbers;

int main()
{
    using namespace std;
    for( int i = 0;  i < 42;  ++i )
    {
        cout << (i > 0? ", " : "") << numbers[i];
    }
    cout << endl;
}

// [numbers.cpp]
int numbers[42] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

विंडोज 7 में यह MinGW g ++ 4.4.1 और Visual C ++ 10.0 दोनों के साथ ठीक से संकलित और लिंक करता है।

चूंकि प्रकार मेल नहीं खाते हैं, इसलिए जब आप इसे चलाते हैं तो प्रोग्राम क्रैश हो जाता है।

विंडोज 7 दुर्घटना संवाद

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

इन-प्रैक्टिस एक्सप्लेनेशन: main.cppएरे में एक पॉइंटर के रूप में व्यवहार किया जाता है, जिसे एरे के समान पते पर रखा जाता है। 32-बिट निष्पादन योग्य के लिए इसका मतलब है कि intसरणी में पहला मान, एक पॉइंटर के रूप में माना जाता है। यानी, में चर होता है, या प्रकट होता है को रोकने के लिए, । यह प्रोग्राम को पता स्थान के बहुत नीचे मेमोरी तक पहुंचने का कारण बनता है, जो पारंपरिक रूप से आरक्षित और जाल-कारण है। परिणाम: आप एक दुर्घटना हो।main.cppnumbers(int*)1

इस त्रुटि का निदान नहीं करने के लिए कंपाइलर पूरी तरह से अपने अधिकारों के भीतर हैं, क्योंकि C ++ 11 .53.5 / 10 कहता है, घोषणाओं के लिए संगत प्रकारों की आवश्यकता के बारे में,

[N3290 .53.5 / 10]
प्रकार की पहचान पर इस नियम के उल्लंघन के लिए निदान की आवश्यकता नहीं होती है।

एक ही पैराग्राफ विवरण की अनुमति देता है:

... एक सरणी ऑब्जेक्ट के लिए घोषणाएं सरणी प्रकार निर्दिष्ट कर सकती हैं जो प्रमुख सरणी बाध्य (8.3.4) की उपस्थिति या अनुपस्थिति से भिन्न होती हैं।

इस अनुमति भिन्नता में एक अनुवाद इकाई में एक सरणी के रूप में एक नाम घोषित करने और किसी अन्य अनुवाद इकाई में एक सूचक के रूप में शामिल नहीं है।

5.2 नुकसान: समय से पहले अनुकूलन ( memsetऔर दोस्त) करना।

अभी तक नहीं लिखा

5.3 ख़तरा: तत्वों की संख्या प्राप्त करने के लिए C मुहावरे का उपयोग करना।

गहन सी अनुभव के साथ यह लिखना स्वाभाविक है ...

#define N_ITEMS( array )   (sizeof( array )/sizeof( array[0] ))

चूंकि arrayपहले तत्व को इंगित करने के लिए एक डिक्रेटर जहां आवश्यक हो, अभिव्यक्ति sizeof(a)/sizeof(a[0])को भी लिखा जा सकता है sizeof(a)/sizeof(*a)। इसका मतलब समान है, और कोई फर्क नहीं पड़ता कि यह कैसे लिखा जाता है यह सरणी के संख्या तत्वों को खोजने के लिए सी मुहावरा है

मुख्य नुकसान: सी मुहावरे प्रकार नहीं हैं। उदाहरण के लिए, कोड ...

#include <stdio.h>

#define N_ITEMS( array ) (sizeof( array )/sizeof( *array ))

void display( int const a[7] )
{
    int const   n = N_ITEMS( a );          // Oops.
    printf( "%d elements.\n", n );
}

int main()
{
    int const   moohaha[]   = {1, 2, 3, 4, 5, 6, 7};

    printf( "%d elements, calling display...\n", N_ITEMS( moohaha ) );
    display( moohaha );
}

एक सूचक को पास करता है N_ITEMS, और इसलिए सबसे अधिक संभावना एक गलत परिणाम पैदा करता है। विंडोज 7 में एक 32-बिट निष्पादन योग्य के रूप में संकलित…

7 तत्व, कॉलिंग डिस्प्ले ...
1 तत्व।

  1. संकलक int const a[7]बस फिर से लिखता है int const a[]
  2. संकलक को फिर int const a[]से लिखता है int const* a
  3. N_ITEMS इसलिए एक पॉइंटर के साथ लगाया जाता है।
  4. एक 32-बिट निष्पादन योग्य sizeof(array)(एक पॉइंटर का आकार) के लिए फिर 4 है।
  5. sizeof(*array)के बराबर है sizeof(int), जो एक 32-बिट निष्पादन योग्य के लिए भी 4 है।

रन समय में इस त्रुटि का पता लगाने के लिए आप क्या कर सकते हैं ...

#include <assert.h>
#include <typeinfo>

#define N_ITEMS( array )       (                               \
    assert((                                                    \
        "N_ITEMS requires an actual array as argument",        \
        typeid( array ) != typeid( &*array )                    \
        )),                                                     \
    sizeof( array )/sizeof( *array )                            \
    )

7 तत्व, कॉलिंग डिस्प्ले ...
अभिकथन विफल: ("N_ITEMS को तर्क के रूप में वास्तविक सरणी की आवश्यकता होती है", टाइप (a)! = Typeid (& * a)), फ़ाइल runtime_detect ion.cpp, लाइन 16

इस एप्लिकेशन ने रनटाइम से असामान्य तरीके से इसे समाप्त करने का अनुरोध किया है।
अधिक जानकारी के लिए कृपया आवेदन की सहायता टीम से संपर्क करें।

रनटाइम त्रुटि का पता लगाने का पता लगाने की तुलना में बेहतर है, लेकिन यह थोड़ा प्रोसेसर समय बर्बाद करता है, और शायद अधिक प्रोग्रामर समय। संकलन समय पर पता लगाने के साथ बेहतर है! और यदि आप C ++ 98 के साथ स्थानीय प्रकार के सरणियों का समर्थन नहीं करने के लिए खुश हैं, तो आप ऐसा कर सकते हैं:

#include <stddef.h>

typedef ptrdiff_t   Size;

template< class Type, Size n >
Size n_items( Type (&)[n] ) { return n; }

#define N_ITEMS( array )       n_items( array )

इस परिभाषा को संकलित करते हुए, G ++ के साथ पहले पूर्ण कार्यक्रम में प्रतिस्थापित किया गया, मुझे मिल गया ...

M: \ count> g ++ compile_time_detection.cpp
compile_time_detection.cpp: फ़ंक्शन में 'शून्य प्रदर्शन ( अंतर int *)':
compile_time_detection.cpp: 14: त्रुटि 'n_items (const int * &)' में कॉल के लिए कोई मिलान फ़ंक्शन नहीं है।

M: \ count> _

यह कैसे काम करता है: सरणी को संदर्भ के द्वारा पारित किया जाता हैn_items , और इसलिए यह पहले तत्व को इंगित करने के लिए क्षय नहीं करता है, और फ़ंक्शन केवल प्रकार द्वारा निर्दिष्ट तत्वों की संख्या वापस कर सकता है।

C ++ 11 के साथ आप इसका उपयोग स्थानीय प्रकार के सरणियों के लिए भी कर सकते हैं, और यह एक सरणी के तत्वों की संख्या खोजने के लिए सुरक्षित C ++ मुहावरा है

5.4 सी ++ 11 और सी ++ 14 नुकसान: एक constexprसरणी आकार फ़ंक्शन का उपयोग करना ।

C ++ 11 के साथ और बाद में यह स्वाभाविक है, लेकिन जैसा कि आप खतरनाक देखेंगे!, C ++ 03 फ़ंक्शन को बदलने के लिए

typedef ptrdiff_t   Size;

template< class Type, Size n >
Size n_items( Type (&)[n] ) { return n; }

साथ में

using Size = ptrdiff_t;

template< class Type, Size n >
constexpr auto n_items( Type (&)[n] ) -> Size { return n; }

जहां महत्वपूर्ण परिवर्तन का उपयोग होता है constexpr, जो इस फ़ंक्शन को एक संकलन समय स्थिर बनाने की अनुमति देता है

उदाहरण के लिए, C ++ 03 फ़ंक्शन के विपरीत, इस तरह के एक संकलन समय का उपयोग उसी आकार के एक सरणी को घोषित करने के लिए किया जा सकता है:

// Example 1
void foo()
{
    int const x[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
    constexpr Size n = n_items( x );
    int y[n] = {};
    // Using y here.
}

लेकिन constexprसंस्करण का उपयोग करके इस कोड पर विचार करें :

// Example 2
template< class Collection >
void foo( Collection const& c )
{
    constexpr int n = n_items( c );     // Not in C++14!
    // Use c here
}

auto main() -> int
{
    int x[42];
    foo( x );
}

ख़तरा: जुलाई 2015 तक उपरोक्त मिगग 64 -.0.0 के साथ संकलित है -pedantic-errors, और, gcc.godbolt.org/ पर ऑनलाइन संकलक के साथ परीक्षण करना, साथ ही क्लैंग 3.0 और क्लैंग 3.2 के साथ, लेकिन क्लैंग 3.3, 3.4 के साथ नहीं। 1, 3.5.0, 3.5.1, 3.6 (आरसी 1) या 3.7 (प्रयोगात्मक)। और विंडोज प्लेटफॉर्म के लिए महत्वपूर्ण है, यह विजुअल C ++ 2015 के साथ संकलित नहीं होता है। इसका कारण संदर्भों के उपयोग के बारे में C ++ 11 / C ++ 14 स्टेटमेंट है।constexpr भावों :

सी ++ 11 सी ++ 14 $ 5.19 / 2 नौ वें डैश

एक सशर्त-अभिव्यक्ति e एक मुख्य स्थिर अभिव्यक्ति है जब तक कि मूल्यांकन e, अमूर्त मशीन (1.9) के नियमों का पालन करते हुए, निम्न अभिव्यक्तियों में से एक का मूल्यांकन करेगा:
        is

  • एक आईडी-एक्सप्रेशन, जो संदर्भ प्रकार के एक चर या डेटा सदस्य को संदर्भित करता है जब तक कि संदर्भ में पूर्ववर्ती प्रारंभ न हो और या तो
    • यह एक स्थिर अभिव्यक्ति या के साथ आरंभिक है
    • यह किसी वस्तु का गैर-स्थैतिक डेटा सदस्य है जिसका जीवनकाल ई के मूल्यांकन के दौरान शुरू हुआ था;

एक हमेशा अधिक क्रिया लिख ​​सकता है

// Example 3  --  limited

using Size = ptrdiff_t;

template< class Collection >
void foo( Collection const& c )
{
    constexpr Size n = std::extent< decltype( c ) >::value;
    // Use c here
}

... लेकिन यह तब विफल हो जाता है जब Collectionकच्चा सरणी नहीं होता है।

गैर-सरणियों वाले संग्रह से निपटने के लिए किसी n_itemsफ़ंक्शन की अधिभारता की आवश्यकता होती है, लेकिन साथ ही, संकलन समय के लिए किसी को सरणी आकार के संकलन समय प्रतिनिधित्व की आवश्यकता होती है। और क्लासिक सी ++ 03 समाधान, जो सी ++ 11 और सी ++ 14 में भी ठीक काम करता है, फ़ंक्शन को उसके परिणाम को मान के रूप में नहीं बल्कि इसके फ़ंक्शन परिणाम प्रकार के माध्यम से रिपोर्ट करने देना है । इस तरह के उदाहरण के लिए:

// Example 4 - OK (not ideal, but portable and safe)

#include <array>
#include <stddef.h>

using Size = ptrdiff_t;

template< Size n >
struct Size_carrier
{
    char sizer[n];
};

template< class Type, Size n >
auto static_n_items( Type (&)[n] )
    -> Size_carrier<n>;
// No implementation, is used only at compile time.

template< class Type, size_t n >        // size_t for g++
auto static_n_items( std::array<Type, n> const& )
    -> Size_carrier<n>;
// No implementation, is used only at compile time.

#define STATIC_N_ITEMS( c ) \
    static_cast<Size>( sizeof( static_n_items( c ).sizer ) )

template< class Collection >
void foo( Collection const& c )
{
    constexpr Size n = STATIC_N_ITEMS( c );
    // Use c here
    (void) c;
}

auto main() -> int
{
    int x[42];
    std::array<int, 43> y;
    foo( x );
    foo( y );
}

के लिए रिटर्न प्रकार की पसंद के बारे में static_n_items: इस कोड का उपयोग नहीं किया जाता है std::integral_constant क्योंकि std::integral_constantपरिणाम सीधे constexprमूल्य के रूप में दर्शाया जाता है , मूल समस्या को फिर से प्रस्तुत करता है। एक Size_carrierवर्ग के बजाय एक फ़ंक्शन को सीधे किसी सरणी में संदर्भ वापस कर सकता है। हालाँकि, हर कोई उस वाक्य रचना से परिचित नहीं है।

नामकरण के बारे में: इस समाधान का हिस्सा है constexpr -invalid- नियत-से-संदर्भ समस्या के लिए संकलन के विकल्प को लगातार स्पष्ट करना है।

उम्मीद है कि oops-there-was-a-reference-to-in-your- constexprissue C ++ 17 के साथ तय किया जाएगा, लेकिन तब तक STATIC_N_ITEMSउपरोक्त मैक्रो पोर्टेबिलिटी जैसे एक मैक्रो , जैसे क्लैंग और विजुअल C ++ कंपाइलर, रिटेनर टाइप सुरक्षा।

संबंधित: मैक्रोज़ स्कोप का सम्मान नहीं करते हैं, इसलिए नाम टकराव से बचने के लिए एक उपसर्ग नाम का उपयोग करना एक अच्छा विचार हो सकता है, जैसे MYLIB_STATIC_N_ITEMS


1
+1 ग्रेट सी कोडिंग टेस्ट: मैंने वीसी ++ ++ 10.0 और जीसीसी 4.1.2 पर 15 मिनट बिताए हैं Segmentation fault... ठीक करने की कोशिश कर रहा हूं ... आखिरकार आपके स्पष्टीकरण पढ़ने के बाद मुझे पता चला / समझा गया! कृपया अपना ib5.2 सेक्शन :-) चीयर्स
ओलिब्रे

अच्छा। एक नोड - countOf के लिए रिटर्न प्रकार ptrdiff_t के बजाय size_t होना चाहिए। यह संभवतः ध्यान देने योग्य है कि C ++ 11/14 में इसे कॉन्स्टैक्स और नॉइसेप्ट होना चाहिए।
रिकी 65

@ Ricky65: C ++ 11 विचारों का उल्लेख करने के लिए धन्यवाद। इन सुविधाओं के लिए विज़ुअल C ++ के आने में देर हो गई है। इस बारे में size_t, कि आधुनिक प्लेटफार्मों के लिए मेरे पास कोई लाभ नहीं है, लेकिन सी और सी ++ के निहित प्रकार रूपांतरण नियमों के कारण इसमें कई समस्याएं हैं। यही है, के ptrdiff_tसाथ समस्याओं से बचने के लिए, बहुत जानबूझकर उपयोग किया जाता है size_t। हालाँकि, किसी को पता होना चाहिए कि g ++ में टेम्प्लेट साइज़ के मिलान की समस्या है जब तक कि यह size_tनहीं है (मुझे नहीं लगता कि यह संकलक-विशिष्ट समस्या गैर के साथ size_tमहत्वपूर्ण है, लेकिन YMMV)।
चीयर्स एंड हीथ। - अल्फ

@Alf। स्टैंडर्ड वर्किंग ड्राफ्ट (N3936) 8.3.4 में मैंने पढ़ा - एक सरणी की सीमा है ... "एक प्रकार की स्थिर स्थिर अभिव्यक्ति :: size_t और इसका मान शून्य से अधिक होगा"।
रिकी 65

@ रेकी: यदि आप असंगति का हवाला दे रहे हैं, तो यह कथन वर्तमान C ++ 11 मानक में नहीं है, इसलिए इस संदर्भ का अनुमान लगाना मुश्किल है, लेकिन विरोधाभास (एक गतिशील रूप से आवंटित सरणी 0, प्रति C + बाउंड का हो सकता है) +11 end5.3.4 / 7) संभवतः C ++ 14 में समाप्त नहीं होगा। ड्राफ्ट बस हैं: ड्राफ्ट। यदि आप इसके बजाय "इसके" के बारे में पूछ रहे हैं, तो यह मूल अभिव्यक्ति को संदर्भित करता है, परिवर्तित नहीं। यदि तीसरे हाथ पर आप इसका उल्लेख करते हैं क्योंकि आपको लगता है कि शायद इस तरह के वाक्य का अर्थ है कि किसी को size_tसरणियों के आकार को निरूपित करने के लिए उपयोग करना चाहिए , बेशक यह नहीं है।
चीयर्स एंड हीथ। - अल्फ

72

सृजन और आरंभीकरण

किसी भी अन्य प्रकार की C ++ ऑब्जेक्ट के साथ, सरणियों को या तो सीधे नामांकित चर में संग्रहीत किया जा सकता है (तब आकार एक संकलन-समय स्थिर होना चाहिए; C ++ वीएलएएएस का समर्थन नहीं करता है ), या उन्हें अनपेक्षित रूप से ढेर पर संग्रहीत किया जा सकता है और अप्रत्यक्ष रूप से पहुँचा जा सकता है। संकेत (केवल तब आकार को रनटाइम पर गणना की जा सकती है)।

स्वचालित सरणियाँ

स्वचालित सरणियाँ ("स्टैक पर रहने वाले एरे") बनाई जाती हैं, जब नियंत्रण का प्रवाह एक गैर-स्थैतिक सिस्टम सरणी चर की परिभाषा से गुजरता है:

void foo()
{
    int automatic_array[8];
}

आरोही क्रम में प्रारंभिक प्रदर्शन किया जाता है। ध्यान दें कि प्रारंभिक मान तत्व प्रकार पर निर्भर करते हैं T:

  • यदि Tकोई POD है (जैसे intउपरोक्त उदाहरण में), तो कोई आरंभ नहीं होता है।
  • अन्यथा, डिफ़ॉल्ट-निर्माता Tसभी तत्वों को इनिशियलाइज़ करता है।
  • यदि Tकोई डिफ़ॉल्ट डिफ़ॉल्ट-निर्माता प्रदान नहीं करता है, तो प्रोग्राम संकलित नहीं करता है।

वैकल्पिक रूप से, प्रारंभिक मान को स्पष्ट रूप से सरणी इनिशियलाइज़र में निर्दिष्ट किया जा सकता है , घुंघराले कोष्ठक से घिरा अल्पविराम से अलग सूची:

    int primes[8] = {2, 3, 5, 7, 11, 13, 17, 19};

चूंकि इस मामले में सरणी आरंभीकरण में तत्वों की संख्या सरणी के आकार के बराबर है, इसलिए आकार को मैन्युअल रूप से निर्दिष्ट करना निरर्थक है। यह संकलक द्वारा स्वचालित रूप से घटाया जा सकता है:

    int primes[] = {2, 3, 5, 7, 11, 13, 17, 19};   // size 8 is deduced

आकार निर्दिष्ट करना और एक छोटा सरणी आरंभीकरण प्रदान करना भी संभव है:

    int fibonacci[50] = {0, 1, 1};   // 47 trailing zeros are deduced

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

स्थैतिक सरणियाँ

स्टेटिक एरेज़ ("डेटा सेगमेंट में रहने वाले सरणियाँ") staticनाम सरणी स्तर पर कीवर्ड और सरणी चर के साथ परिभाषित स्थानीय सरणी चर हैं ("वैश्विक चर"):

int global_static_array[8];

void foo()
{
    static int local_static_array[8];
}

(ध्यान दें कि नेमस्पेस स्कोप पर चर स्पष्ट रूप से स्थिर होते हैं। staticकीवर्ड को उनकी परिभाषा में जोड़ना पूरी तरह से अलग है, जिसका अर्थ है )।

यहाँ बताया गया है कि कैसे स्थिर सरणियाँ स्वचालित सरणियों से भिन्न व्यवहार करती हैं:

  • एक सरणी इनिशलाइज़र के बिना स्टेटिक सरणियों को किसी भी संभावित संभावित आरंभीकरण से पहले शून्य-आरंभीकृत किया जाता है।
  • स्टेटिक POD सरणियों को एक बार ठीक किया जाता है , और प्रारंभिक मूल्यों को आमतौर पर निष्पादन योग्य में बेक किया जाता है, इस स्थिति में रनटाइम पर कोई प्रारंभ लागत नहीं होती है। यह हमेशा सबसे अंतरिक्ष-कुशल समाधान नहीं है, हालांकि, और मानक द्वारा इसकी आवश्यकता नहीं है।
  • स्थिर गैर-पीओडी सरणियों को पहली बार नियंत्रित किया जाता है जब नियंत्रण का प्रवाह उनकी परिभाषा से गुजरता है। स्थानीय स्थैतिक सरणियों के मामले में, ऐसा कभी नहीं हो सकता है यदि फ़ंक्शन को कभी नहीं कहा जाता है।

(उपरोक्त में से कोई भी सरणियों के लिए विशिष्ट नहीं है। ये नियम अन्य प्रकार की स्थैतिक वस्तुओं पर भी समान रूप से लागू होते हैं।)

डेटा सदस्यों को सरणी

सरणी डेटा सदस्य तब बनाए जाते हैं जब उनका अपना ऑब्जेक्ट बनाया जाता है। दुर्भाग्य से, C ++ 03 सदस्य आरंभीकरण सूची में सरणियों को आरम्भ करने का कोई साधन प्रदान नहीं करता है , इसलिए आरंभीकरण को असाइनमेंट के साथ नकली किया जाना चाहिए:

class Foo
{
    int primes[8];

public:

    Foo()
    {
        primes[0] = 2;
        primes[1] = 3;
        primes[2] = 5;
        // ...
    }
};

वैकल्पिक रूप से, आप कंस्ट्रक्टर बॉडी में एक स्वचालित सरणी को परिभाषित कर सकते हैं और तत्वों को कॉपी कर सकते हैं:

class Foo
{
    int primes[8];

public:

    Foo()
    {
        int local_array[] = {2, 3, 5, 7, 11, 13, 17, 19};
        std::copy(local_array + 0, local_array + 8, primes + 0);
    }
};

C ++ 0x में, सरणियों कर सकते हैं प्रारंभकर्ता को सूची धन्यवाद सदस्य में प्रारंभ वर्दी प्रारंभ :

class Foo
{
    int primes[8];

public:

    Foo() : primes { 2, 3, 5, 7, 11, 13, 17, 19 }
    {
    }
};

यह एकमात्र समाधान है जो ऐसे तत्व प्रकारों के साथ काम करता है जिनमें कोई डिफ़ॉल्ट निर्माता नहीं है।

गतिशील सरणियाँ

डायनेमिक सरणियों का कोई नाम नहीं है, इसलिए उन्हें एक्सेस करने का एकमात्र साधन पॉइंटर्स के माध्यम से है। क्योंकि उनके पास कोई नाम नहीं है, मैं उन्हें अब से "अनाम सरणियों" के रूप में संदर्भित करूंगा।

सी में, गुमनाम सरणियों के माध्यम से mallocऔर दोस्तों को बनाया जाता है । C ++ में, अनाम सरणियों को new T[size]सिंटैक्स का उपयोग करके बनाया जाता है जो एक अनाम सरणी के पहले तत्व को एक पॉइंटर लौटाता है:

std::size_t size = compute_size_at_runtime();
int* p = new int[size];

निम्न ASCII कला स्मृति लेआउट को दर्शाती है यदि आकार रनटाइम पर 8 के रूप में गणना की जाती है:

             +---+---+---+---+---+---+---+---+
(anonymous)  |   |   |   |   |   |   |   |   |
             +---+---+---+---+---+---+---+---+
               ^
               |
               |
             +-|-+
          p: | | |                               int*
             +---+

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

ध्यान दें कि यहां कोई सरणी-से-पॉइंटर क्षय नहीं हो रहा है। यद्यपि मूल्यांकन new int[size]वास्तव में पूर्णांक का एक सरणी बनाता है , अभिव्यक्ति new int[size]का परिणाम पहले से ही एक पूर्णांक (पहला तत्व) के लिए एक संकेतक है, पूर्णांक की एक सरणी नहीं है या अज्ञात आकार के पूर्णांक के एक सरणी के लिए सूचक नहीं है। यह असंभव होगा, क्योंकि स्थैतिक प्रकार प्रणाली को संकलन-समय स्थिरांक के लिए सरणी आकार की आवश्यकता होती है। (इसलिए, मैंने चित्र में स्थिर प्रकार की जानकारी के साथ अनाम सरणी को एनोटेट नहीं किया है।)

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

int* p = new int[some_computed_size]();

(अर्धविराम से ठीक पहले कोष्ठक की अनुगामी जोड़ी पर ध्यान दें।) फिर से, C ++ 0x नियमों को सरल करता है और एकरूपता के लिए अनाम सरणियों के लिए प्रारंभिक मान निर्दिष्ट करने की अनुमति देता है:

int* p = new int[8] { 2, 3, 5, 7, 11, 13, 17, 19 };

यदि आप एक अनाम सरणी का उपयोग कर रहे हैं, तो आपको इसे सिस्टम में वापस करना होगा:

delete[] p;

आपको प्रत्येक अनाम सरणी को बिल्कुल एक बार रिलीज़ करना होगा और फिर बाद में इसे कभी नहीं छूना चाहिए। स्मृति रिसाव (या आमतौर पर, तत्व प्रकार, संसाधन रिसाव के आधार पर) के सभी परिणामों में इसे जारी नहीं करना और इसे कई बार जारी करने की कोशिश करना अपरिभाषित व्यवहार का परिणाम है। गैर-सरणी फ़ॉर्म delete(या free) delete[]को रिलीज़ करने के बजाय सरणी का उपयोग करना भी अपरिभाषित व्यवहार है


2
की निंदा staticनाम स्थान दायरे में उपयोग सी ++ 11 में हटा दिया गया था।
किंवदंतियां 2

क्योंकि newमैं ऑपरेटर हूँ, यह निश्चित रूप से संदर्भ द्वारा आवंटित सरणी वापस कर सकता है। इसका कोई मतलब नहीं है ...
Deduplicator

@Deduplicator नहीं, यह नहीं हो सकता, क्योंकि ऐतिहासिक रूप से, newसंदर्भों की तुलना में बहुत पुराना है।
fredoverflow

@FredOverflow: तो एक कारण है कि यह एक संदर्भ नहीं लौटा सकता है, यह लिखित स्पष्टीकरण से बिल्कुल अलग है।
डेडुप्लिकेटर

2
@Deduplicator मुझे नहीं लगता कि अज्ञात सीमा की एक सरणी का संदर्भ मौजूद है। कम से कम g ++ संकलन के लिए मना कर देता हैint a[10]; int (&r)[] = a;
fredoverflow
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.