c - चार सरणी के लिए मेमोरी आवंटन




arrays char (4)

मैं कैसे सरणी की घोषणा कर सकता हूं और फिर इसका आकार निर्धारित कर सकता हूं?

मत करो; 'घोषणा' जब आप जानते हैं कि किस आकार का होना जरूरी है आप इससे पहले भी इसका उपयोग नहीं कर सकते।

C99 और बाद में, जब आवश्यक हो तो आप चर को परिभाषित कर सकते हैं - कहीं भी किसी स्टेटमेंट ब्लॉक में आप वीएलए (वेरिएबल-लाइन एरेज़) का भी उपयोग कर सकते हैं जहां आकार रनटाइम तक ज्ञात नहीं है। वीएलए (जैसे 1 MiB या अधिक के रूप में - लेकिन अपनी मशीन और पूर्वाग्रहों के अनुरूप सीमा को सुदृढ़ करें) के रूप में भारी एरेज़ न बनाएं; सभी के बाद गतिशील स्मृति आवंटन का उपयोग करें

यदि आप पुराने C89 / C90 मानक के साथ फंस रहे हैं, तो आप केवल एक ब्लॉक की शुरुआत में चर को परिभाषित कर सकते हैं, और arrays के संकलन समय पर ज्ञात आकार हैं, इसलिए आपको डायनामिक स्मृति आवंटन - malloc() , free() आदि।

मेरे सी भाषा के साथ बड़ी समस्या है जब यह स्ट्रिंग की बात आती है, char * या जो भी ... तो इस विशेष मामले में मेरे पास एक बड़ी समस्या है। मैं वर्णों की एक सरणी बनाना चाहता हूं और मुझे अभी तक पता नहीं है कि यह आकार कितना होगा। मैं इस तरह कुछ लिखना चाहता हूं:

char test[];

लेकिन, इसके बाद, जब मुझे पता चलेगा कि वहाँ कितने तत्व होंगे, मैं इस तरह कुछ लिखना चाहता हूं:

char test[num_of_elements];

हालांकि मुझे पता है कि यह गलत है और मैं ऐसा करने में सक्षम नहीं होगा। तो, मैं यह कैसे कर सकता हूँ? मैं कैसे सरणी की घोषणा कर सकता हूं और फिर इसका आकार निर्धारित कर सकता हूं?


आपके (i) टूल-चेन और (ii) पर निर्भर करता है कि (ii) कैसे और कब आप आकार को जानते होंगे - आपके पास या तो (ए) चर लंबाई एरेज़ या (बी) डायनामिक मेमोरी आवंटन फ़ंक्शंस का उपयोग करने का विकल्प होता है।

अगर (उपकरण-श्रृंखला C99 और उसके बाद का समर्थन करता है) या (आप रनटाइम पर सरणी लंबाई जान लेंगे) चर लंबाई की सरणी का उपयोग करें

यदि (पुराने उपकरण-श्रृंखला) या (आप आवंटन और स्मृति को जारी करने की लचीलापन चाहते हैं) डायनामिक मेमोरी आवंटन फ़ंक्शन का उपयोग करें

यहाँ नमूने हैं

1 चर लंबाई सरणी

void f(int m, char C[m][m])
{
    char test[m];
    :
}

या

2 गतिशील मेमोरी आवंटन फ़ंक्शन का उपयोग करना

void somefunc(int n)
{
    char *test;

    test = malloc (n * sizeof (char));

    // check for test != null

    // use test

    free (test);
}

के रूप में वीएलए का उपयोग कर लिखा जा सकता है

int n = 5;
char test[n];

पहले एक "चार" के लिए एक संकेतक घोषित करें फिर पूछें (प्रणाली) अंतरिक्ष के लिए अपने आवश्यक मूल्यों को malloc का उपयोग करने के लिए और फिर इस "सरणी" में तत्वों को जोड़ने के लिए पूछें।

char * test;
int num_of_elements = 99;
test = malloc(sizeof(char) * num_of_elements); 
//test points to first array elament
test[0] = 11;
test[1] = 22;
//etc

स्टेटिक कैरेक्टर अर्रे घोषित करना (तार)

जब आप जानते हैं (या उचित विचार है कि आपके ऑरेंज को कितना बड़ा होना चाहिए, तो आप बस अपने इनपुट को संभालने के लिए पर्याप्त आकार की एक सरणी घोषित कर सकते हैं (अर्थात यदि आपके नाम 25 वर्णों से अधिक नहीं हैं, तो आप सुरक्षित रूप से name[26] घोषित कर सकते हैं name[26] । स्ट्रिंग्स के लिए, आपको हमेशा 1 + (रिक्त समाप्ति वाले चरित्र के लिए) number of chars to store कम से कम वर्णों की आवश्यकता होती है।

यदि 25 से अधिक कुछ वर्ण हो सकते हैं, तो सरणी के अंत से परे आकस्मिक लेखन के खिलाफ रक्षा करने के लिए आवश्यक होने के बावजूद आपके ऑरलाइन को कुछ बाइट्स देने में कुछ भी गलत नहीं है। name[32] कहें

चलो नीचे 5-characters की एक सरणी घोषित करते हैं और देखें कि कैसे जानकारी स्मृति में संग्रहीत है

char name[5] = {0}; /* always initialize your arrays */

उपरोक्त घोषणा आपके उपयोग के लिए स्टैक पर 5-contiguous bytes की एक सरणी बनाता है। उदाहरण के लिए, आप निम्नानुसार स्मृति के 5-बाइट को आरंभ करने के लिए कल्पना कर सकते हैं:

        +---+---+---+---+---+
 name   | 0 | 0 | 0 | 0 | 0 |
        +---+---+---+---+---+
          \
           'name' holds the starting address for
            the block of memory that is the array
            (i.e. the address of the first element
             like: 0x7fff050bf3d0)

नोट: name का प्रयोग करते समय 'वर्ण स्ट्रिंग' रखने के लिए, वास्तविक स्ट्रिंग 4 वर्णों से अधिक नहीं हो सकती है क्योंकि आपको एक रिक्त-टर्मिनेटरिंग वर्ण के साथ एक स्टिंग को समाप्त करना होगा , जो शून्य वर्ण '\0' (या केवल संख्यात्मक 0 , दोनों बराबर हैं)

name जानकारी संग्रहीत करने के लिए, आप एक-एक-एक-समय में वर्णों को निर्दिष्ट करके कर सकते हैं:

name[0] = 'J';
name[1] = 'o';
name[2] = 'h';
name[3] = 'n';
name[4] =  0;   /* null-terminate. note: this was already done by initialization */

स्मृति में अब आपके पास है:

        +---+---+---+---+---+
 name   | J | o | h | n | 0 |    /* you actually have the ASCII value for */
        +---+---+---+---+---+    /* each letter stored in the elements    */

बेशक, कोई भी इस तरह से एक समय में एक चरित्र को निर्दिष्ट नहीं करता है। सी के द्वारा दिए गए कार्यों में से किसी एक का उपयोग करते हुए आप कई विकल्प getline , getline , getline , getline , या फ़ाइल स्ट्रीम से जानकारी पढ़कर, या फाइल डिस्क्रिप्टर को getline , getline या एक सरल loop और index variable का इस्तेमाल करके असाइनमेंट, या किसी स्ट्रिंग फ़ॉर्मेटिंग फ़ंक्शंस का उपयोग करके, उदा। sprintf , आदि ... उदाहरण के लिए आप निम्न में से किसी के साथ एक ही चीज़ को पूरा कर सकते हैं:

/* direct copy */
strcpy (name, "John");
strncpy (name, "John", 5);
memcpy (name, "John", sizeof "John");  /* include copy of the terminating char */

/* read from stdin into name */
printf ("Please enter a name (4 char max): ");
scanf ("%[^\n]%*c", name);

नोट: ऊपर strncpy साथ, यदि आपने सभी तत्व को 0 से प्रारंभ नहीं किया है (जो की अंतिम आपकी null-terminating वर्ण के रूप में कार्य करेगा, और तब strncpy (name, "John", 4); आपको मैन्युअल रूप से समाप्त करने की आवश्यकता होगी name[4] = 0; साथ स्ट्रिंग name[4] = 0; अन्यथा आपके पास एक वैध स्ट्रिंग नहीं होती है (आपके पास वर्णों का एक अपरिवर्तित सरणी होती है जो undefined behavior को जन्म देगी यदि आप name उपयोग करते हैं जहां स्ट्रिंग की उम्मीद थी।)

यदि आप STOP को स्पष्ट रूप से नहीं समझते हैं, तो पढ़ें और समझें कि एक null-terminated string क्या है और यह array of characters से कैसे अलग है। गंभीरता से, अब बंद करो और सीखें, यह सी के लिए मौलिक है (यदि यह एक null-terminating वर्ण के साथ समाप्त नहीं null-terminating है - यह सी-स्ट्रिंग नहीं है

अगर मुझे नहीं पता कि मुझे कितने अक्षरों को स्टोर करना है?

वर्ण स्ट्रिंग्स की डायनेमिक आवंटन

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

एक सरल उदाहरण में मदद मिलेगी अधिकांश स्मृति आवंटन / नि: शुल्क कार्यों को stdlib.h में घोषित किया stdlib.h

char *name = NULL;  /* declare a pointer, and initialize to NULL */

name = malloc (5 * sizeof *name); /* allocate a 5-byte block of memory for name */

if (!name) {    /* validate memory was allocated -- every time */
    fprintf (stderr, "error: name allocation failed, exiting.\n");
    exit (EXIT_FAILURE);
}

/* Now use name, just as you would the statically declared name above */
strncpy (name, "John", 5);

printf (" name contains: %s\n", name);

free (name);    /* free memory when no longer needed.
                   (if reusing name, set 'name = NULL;') 
                 */

नोट: malloc यह आवंटित स्मृति की सामग्री को प्रारंभ नहीं करता है। यदि आप शून्य के साथ अपने नए स्मृति ब्लॉक को प्रारंभ करना चाहते हैं (जैसा कि हमने स्थिर सरणी के साथ किया था), फिर calloc उपयोग calloc बजाय आप malloc उपयोग कर सकते हैं और उसके बाद memset को भी कॉल कर सकते हैं।

यदि मैं मेमोरी आवंटित करता हूं तो क्या होता है, फिर क्या ज़रूरत है?

जैसा कि गतिशील स्मृति पर चर्चा से ऊपर वर्णित है, सामान्य योजना एक उचित अनुमानित राशि आवंटित करने के लिए है, फिर realloc रूप में आवश्यक है आप malloc द्वारा बनाए गए स्मृति के मूल ब्लॉक को पुनः निर्दिष्ट करने के लिए realloc का उपयोग करें realloc मूलतः स्मृति का एक नया ब्लॉक बनाता है, स्मृति को अपने पुराने ब्लॉक से नए तक कॉपी करता है, और फिर स्मृति के पुराने ब्लॉक को मुक्त करता है। चूंकि स्मृति का पुराना ब्लॉक मुक्त है , इसलिए आप फिर से आवंटन के लिए एक अस्थायी सूचक का उपयोग करना चाहते हैं। यदि पुन: नियोजन विफल हो जाता है, तो आपके पास अभी भी आपके लिए उपलब्ध स्मृति का मूल ब्लॉक है।

आप किसी भी कॉल में realloc लिए जितनी चाहें उतनी छोटी या जितनी मेमोरी जोड़ सकते हैं। आम तौर पर देखा जाने वाला मानक योजना कुछ प्रारंभिक आवंटन के साथ शुरू होता है, फिर हर बार जब आप बाहर निकलते हैं तो दो बार दोबारा आवंटित करें। (इसका मतलब है कि आपको वर्तमान में आवंटित कितना मेमोरी का ट्रैक रखने की आवश्यकता है)।

इसे सीवन करने के लिए, एक सरल उदाहरण के साथ समाप्त करें, जो प्रोग्राम की पहली तर्क के रूप में किसी भी लम्बाई की स्ट्रिंग पढ़ता है ( " उद्धरण " उपयोग करें यदि आपकी स्ट्रिंग में सफेद स्थान है)। यह तब स्ट्रिंग को रखने के लिए स्थान को आवंटित करेगा, फिर मूल स्ट्रिंग के अंत में अधिक पाठ जोड़ने के लिए पुन: निर्दिष्ट करें। अंत में यह सभी मेमोरी को बाहर निकलने से पहले उपयोग में लाएगा:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc, char **argv) {

    if (argc < 2) { /* validate input */
        fprintf (stderr, "error: insufficient input.  usage: %s \"name\"\n",
                 argv[0]);
        return 1;
    }

    size_t len = strlen (argv[1]);  /* length of input  */
    size_t sz_mem = len + 1;        /* memory required  */

    char *name = malloc (sz_mem * sizeof *name);  /* allocate memory for name */

    if (!name) {    /* validate memory created successfully or throw error */
        fprintf (stderr, "error: virtual memory exhausted allocating 'name'\n");
        return 1;
    }

    printf ("\n allocated %zu bytes of memory for 'name'\n", sz_mem);
    memset (name, 0, sz_mem); /* initialize memory to zero (optional) */

    strncpy (name, argv[1], sz_mem);  /* copy the null-terminator as well */
    printf (" name: '%s' (begins at address: %p)\n", name, name);

    /* realloc - make name twice as big */
    void *tmp = realloc (name, 2 * sz_mem);  /* use a temporary pointer */
    if (!tmp) {                              /* check realloc succeeded */
        fprintf (stderr, "error: virtual memory exhausted, realloc 'name'\n");
        return 1;
    }
    memset (tmp + sz_mem, 0, sz_mem * sizeof *name); /* zero new memory */
    name = tmp;         /* assign new block to name       */
    sz_mem += sz_mem;   /* update current allocation size */

    printf (" reallocated 'name' to %zu bytes\n", sz_mem);
    strncat (name, " reallocated", sizeof " reallocated");

    printf ("\n final name : '%s'\n\n", name);

    free (name);

    return 0;
}

उपयोग / आउटपुट

$ ./bin/arraybasics "John Q. Public"

 allocated 15 bytes of memory for 'name'
 name: 'John Q. Public' (begins at address: 0xf17010)
 reallocated 'name' to 30 bytes

 final name : 'John Q. Public reallocated'

मेमोरी चेक

जब आप गतिशील रूप से स्मृति आवंटित करते हैं, तो यह मान्य है कि आप मेमोरी का सही ढंग से उपयोग कर रहे हैं और आप आवंटित सभी मेमोरी को ट्रैक और मुक्त कर सकते हैं। मेमोरी त्रुटि परीक्षक का उपयोग वाल्ग्रिंड को अपनी मेमोरी उपयोग को बढ़ाने के लिए करना सही है। (इसमें कोई बहाना नहीं है, यह मर चुका है- ऐसा करने के लिए आसान है) बस अपने valgrind yourprogramexe

$ valgrind ./bin/arraybasics "John Q. Public"
==19613== Memcheck, a memory error detector
==19613== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==19613== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==19613== Command: ./bin/arraybasics John\ Q.\ Public
==19613==

 allocated 15 bytes of memory for 'name'
 name: 'John Q. Public' (begins at address: 0x51e0040)
 reallocated 'name' to 30 bytes

 final name : 'John Q. Public reallocated'

==19613==
==19613== HEAP SUMMARY:
==19613==     in use at exit: 0 bytes in 0 blocks
==19613==   total heap usage: 2 allocs, 2 frees, 45 bytes allocated
==19613==
==19613== All heap blocks were freed -- no leaks are possible
==19613==
==19613== For counts of detected and suppressed errors, rerun with: -v
==19613== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

उत्पादन में, निम्न पंक्तियां विशेष महत्व के हैं:

==19613== HEAP SUMMARY:
==19613==     in use at exit: 0 bytes in 0 blocks
==19613==   total heap usage: 2 allocs, 2 frees, 45 bytes allocated

यह आपको बताता है कि आपके कार्यक्रम के दौरान आवंटित सभी स्मृति को ठीक से मुक्त किया गया है। (सुनिश्चित करें कि आप सभी खुली फ़ाइल धाराओं को बंद करते हैं, वे गतिशील रूप से आवंटित भी हैं)।

समान महत्व का ERROR SUMMARY :

==19613== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

स्मृति उपयोग में कोई त्रुटियां नहीं हैं यदि आप अपने ब्लॉक के बाहर के किसी स्थान से या किसी प्रारंभिक स्थान से पढ़ने, लिखने या नि: शुल्क मेमोरी करने का प्रयास करते हैं या अन्य स्मृति से बाहर नहीं निकले, तो यह जानकारी एक त्रुटि के रूप में दिखाई जाएगी

(दबड़ा हुआ: 2 से 2 बस मेरे सिस्टम पर मौजूद अतिरिक्त डीबग पुस्तकालयों से संबंधित नहीं है)

यह इरादा से अधिक लंबा था, लेकिन अगर यह मदद करता है, तो इसके लायक था। सौभाग्य।





char