2011-08-28 10 views
9

मुझे PHP में उपयोग किया जाता है, लेकिन मैं सी सीखना शुरू कर रहा हूं। मैं एक प्रोग्राम बनाने की कोशिश कर रहा हूं जो लाइन द्वारा फ़ाइल लाइन पढ़ता है और प्रत्येक पंक्ति को सरणी में संग्रहीत करता है।सी में गतिशील आकार का सरणी कैसे बनाएं?

अभी तक मेरे पास एक प्रोग्राम है जो फ़ाइल लाइन को लाइन से पढ़ता है, और यहां तक ​​कि प्रत्येक पंक्ति को प्रिंट करता है, लेकिन अब मुझे प्रत्येक पंक्ति को सरणी में जोड़ने की आवश्यकता है।

मेरी दोस्त कल रात मुझे इसके बारे में कुछ बता रही थी। उन्होंने कहा कि मुझे सी में एक बहुआयामी सरणी का उपयोग करना होगा, इसलिए मूल रूप से array[x][y][y] भाग स्वयं ही आसान है, क्योंकि मुझे पता है कि प्रत्येक पंक्ति होगी कि अधिकतम बाइट्स। हालांकि, मुझे नहीं पता कि कितनी लाइन फ़ाइल होगी।

मुझे लगता है कि मैं इसे फ़ाइल के माध्यम से लूप कर सकता हूं और हर बार एक पूर्णांक बढ़ा सकता हूं और इसका उपयोग कर सकता हूं, लेकिन मुझे लगता है कि ऐसा करने का एक और आसान तरीका हो सकता है।

किसी भी विचार या सही दिशा में एक संकेत भी? मैं किसी भी मदद की सराहना करता हूं।

+0

आप समारोह 'उपयोग कर सकते हैं पर बाद में सरणी का आकार बदलने के realloc'। – Jonathon

+0

मैं उस फ़ंक्शन को देखता हूं और सोचने की कोशिश करता हूं कि मैं इसे कैसे कार्यान्वित कर सकता हूं और मैं आपको वापस आऊंगा, धन्यवाद – Rob

उत्तर

9

गतिशील रूप से एक 2 डी सरणी का आवंटन करने के लिए:

char **p; 
int i, dim1, dim2; 


/* Allocate the first dimension, which is actually a pointer to pointer to char */ 
p = malloc (sizeof (char *) * dim1); 

/* Then allocate each of the pointers allocated in previous step arrays of pointer to chars 
* within each of these arrays are chars 
*/ 
for (i = 0; i < dim1; i++) 
    { 
    *(p + i) = malloc (sizeof (char) * dim2); 
    /* or p[i] = malloc (sizeof (char) * dim2); */ 
    } 

/* Do work */ 

/* Deallocate the allocated array. Start deallocation from the lowest level. 
* that is in the reverse order of which we did the allocation 
*/ 
for (i = 0; i < dim1; i++) 
{ 
    free (p[i]); 
} 
free (p); 

संशोधित उपरोक्त विधि। जब आपको जोड़ने के लिए दूसरी पंक्ति की आवश्यकता होती है तो *(p + i) = malloc (sizeof (char) * dim2); करें और i अपडेट करें। इस मामले में आपको फ़ाइल में लाइनों की अधिकतम संख्या की भविष्यवाणी करने की आवश्यकता है जो dim1 चर द्वारा इंगित किया गया है, जिसके लिए हम पहली बार p सरणी आवंटित करते हैं। यह केवल (sizeof (int *) * dim1) बाइट आवंटित करेगा, इस प्रकार char p[dim1][dim2] (सी 99 में) से बेहतर विकल्प होगा।

मुझे लगता है कि एक और तरीका है। ब्लॉक में सरणी आवंटित करें और ओवरफ्लो होने पर उन्हें चेन करें।

struct _lines { 
    char **line; 
    int n; 
    struct _lines *next; 
} *file; 

file = malloc (sizeof (struct _lines)); 
file->line = malloc (sizeof (char *) * LINE_MAX); 
file->n = 0; 
head = file; 

इसके बाद पहला ब्लॉक उपयोग करने के लिए तैयार है। जब आप एक लाइन डालने की आवश्यकता सिर्फ कार्य करें:

/* get line into buffer */ 
file.line[n] = malloc (sizeof (char) * (strlen (buffer) + 1)); 
n++; 

जाता है nLINE_MAX एक और ब्लॉक का आवंटन और यह एक से लिंक करें।

struct _lines *temp; 

temp = malloc (sizeof (struct _lines)); 
temp->line = malloc (sizeof (char *) * LINE_MAX); 
temp->n = 0; 
file->next = temp; 
file = file->next; 

ऐसा कुछ।

जब एक ब्लॉक का n0 बन जाता है, इसे हटा दें, और मौजूदा ब्लॉक सूचक file को पिछले एक को अपडेट करें। आप या तो एकल लिंक्ड सूची शुरू करने से शुरू हो सकते हैं और शुरुआत से ट्रैवर्स या डबल लिंक का उपयोग कर सकते हैं।

+0

बाद वाला दृष्टिकोण यह है कि जीएनयू की 'पूंछ (1)' कार्यान्वयन कैसे काम करता है, मेरा मानना ​​है कि जब इनपुट फ़ाइल गैर-तलाश योग्य (जैसे पाइप या स्टडीन) होती है। चूंकि यह केवल उन मामलों में इनपुट फ़ाइल के माध्यम से एक पास कर सकता है, यह फ़ाइल को मेमोरी ब्लब्स की एक लिंक्ड सूची में संग्रहीत करता है, और जब यह फ़ाइल के अंत में हिट करता है, तो यह पिछली 'एन' लाइनों को प्रिंट करने के लिए पीछे की ओर खोज करता है। –

+0

@ एडम रोसेनफील्ड: यह नहीं पता था, जानकारी के लिए धन्यवाद। मैंने इसे बहुत पहले स्मृति में शब्दों की एक लंबी सूची स्टोर करने के लिए उपयोग किया था, और यह बहुत उपयोगी है। – phoxis

1

यदि आप सी का उपयोग कर रहे हैं तो आपको स्वयं को सरणी का आकार बदलने की आवश्यकता होगी। सी ++ और एसडीएल ने यह आपके लिए किया है। इसे vector कहा जाता है। http://www.cplusplus.com/reference/stl/vector/

+0

दुर्भाग्यवश, मैं सी से चिपकने की कोशिश कर रहा हूं, लेकिन धन्यवाद। – Rob

5

सी में कोई मानक आकार बदलने योग्य सरणी प्रकार नहीं है। आपको इसे स्वयं लागू करना होगा, या किसी तृतीय-पक्ष लाइब्रेरी का उपयोग करना होगा। यहाँ एक सरल नंगे हड्डियों उदाहरण है:

typedef struct int_array 
{ 
    int *array; 
    size_t length; 
    size_t capacity; 
} int_array; 

void int_array_init(int_array *array) 
{ 
    array->array = NULL; 
    array->length = 0; 
    array->capacity = 0; 
} 

void int_array_free(int_array *array) 
{ 
    free(array->array); 
    array->array = NULL; 
    array->length = 0; 
    array->capacity = 0; 
} 

void int_array_push_back(int_array *array, int value) 
{ 
    if(array->length == array->capacity) 
    { 
     // Not enough space, reallocate. Also, watch out for overflow. 
     int new_capacity = array->capacity * 2; 
     if(new_capacity > array->capacity && new_capacity < SIZE_T_MAX/sizeof(int)) 
     { 
      int *new_array = realloc(array->array, new_capacity * sizeof(int)); 
      if(new_array != NULL) 
      { 
       array->array = new_array; 
       array->capacity = new_capacity; 
      } 
      else 
       ; // Handle out-of-memory 
     } 
     else 
      ; // Handle overflow error 
    } 

    // Now that we have space, add the value to the array 
    array->array[array->length] = value; 
    array->length++; 
} 

इस तरह यह प्रयोग करें:

int_array a; 
int_array_init(&a); 

int i; 
for(i = 0; i < 10; i++) 
    int_array_push_back(&a, i); 
for(i = 0; i < a.length; i++) 
    printf("a[%d] = %d\n", i, a.array[i]); 

int_array_free(&a); 
बेशक

, यह केवल int रों की एक सरणी के लिए है।चूंकि सी में टेम्पलेट्स नहीं हैं, इसलिए आपको या तो प्रत्येक कोड को प्रत्येक अलग-अलग प्रकार के सरणी के लिए मैक्रो में रखना होगा (या GNU m4 जैसे विभिन्न प्रीप्रोसेसर का उपयोग करना होगा)। या, आप एक जेनेरिक सरणी कंटेनर का उपयोग कर सकते हैं जो या तो void* पॉइंटर्स (सभी सरणी तत्वों को malloc 'ed) या अपारदर्शी मेमोरी ब्लब्स की आवश्यकता होती है, जिसके लिए प्रत्येक तत्व पहुंच/सेट के लिए प्रत्येक तत्व पहुंच और memcpy के साथ एक कलाकार की आवश्यकता होगी।

किसी भी मामले में, यह सुंदर नहीं है। द्वि-आयामी सरणी भी उलझन में हैं।

0

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

जब तक आप अधिक यादृच्छिक पहुंच (जो ओ (एन) है) करने की योजना नहीं बनाते हैं, तो पुनरावृत्ति नियमित सरणी के रूप में सरल होती है।

typedef struct Line Line; 
struct Line{ 
    char text[LINE_MAX]; 
    Line *next; 
}; 

Line *mkline() 
{ 
    Line *l = malloc(sizeof(Line)); 
    if(!l) 
     error(); 
    return l; 
} 

main() 
{ 
    Line *lines = mkline(); 
    Line *lp = lines; 
    while(fgets(lp->text, sizeof lp->text, stdin)!=NULL){ 
     lp->next = mkline(); 
     lp = lp->next; 
    } 
    lp->next = NULL; 
} 
0

एक बहुआयामी सरणी इस समस्या को हल कर सकते हैं, एक आयताकार 2 डी सरणी वास्तव में प्राकृतिक सी समाधान नहीं होगा।

यहां एक ऐसा प्रोग्राम है जो प्रारंभ में फ़ाइल को एक लिंक्ड सूची में पढ़ता है, और फिर सही आकार के पॉइंटर्स के वेक्टर आवंटित करता है। प्रत्येक व्यक्तिगत चरित्र तब array[line][col] के रूप में दिखाई देता है लेकिन असल में प्रत्येक पंक्ति केवल तब तक होती है जब तक इसकी आवश्यकता होती है। <err.h> को छोड़कर यह सी 99 है।

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

typedef struct strnode { 
    char *s; 
    struct strnode *next; 
} strnode; 

strnode *list_head; 
strnode *list_last; 

strnode *read1line(void) { 
    char space[1024]; 
    if(fgets(space, sizeof space, stdin) == NULL) 
    return NULL; 
    strnode *node = malloc(sizeof(strnode)); 
    if(node && (node->s = malloc(strlen(space) + 1))) { 
    strcpy(node->s, space); 
    node->next = NULL; 
    if (list_head == NULL) 
     list_head = node; 
    else 
     list_last->next = node; 
    list_last = node; 
    return node; 
    } 
    err(1, NULL); 
} 

int main(int ac, char **av) { 
    int n; 
    strnode *s; 

    for(n = 0; (s = read1line()) != NULL; ++n) 
    continue; 
    if(n > 0) { 
    int i; 
    strnode *b; 
    char **a = malloc(n * sizeof(char *)); 
    printf("There were %d lines\n", n); 
    for(b = list_head, i = 0; b; b = b->next, ++i) 
     a[i] = b->s; 
    printf("Near the middle is: %s", a[n/2]); 
    } 
    return 0; 
} 
0

आप malloc और realloc कार्यों का उपयोग गतिशील रूप से आवंटित और char की ओर इशारा की एक सरणी आकार बदलने के लिए कर सकते हैं और सरणी के प्रत्येक तत्व एक स्ट्रिंग फ़ाइल (जहां कि स्ट्रिंग के भंडारण भी आवंटित किया जाता है से पढ़ने के लिए इंगित करेगा गतिशील रूप से)। सादगी के लिए हम मान लेंगे कि प्रत्येक पंक्ति की अधिकतम लंबाई एम वर्णों से कम है (नई लाइन गिनती है), इसलिए हमें व्यक्तिगत लाइनों के गतिशील आकार बदलने की आवश्यकता नहीं है।

प्रत्येक बार जब आप इसे विस्तारित करते हैं तो आपको मैन्युअल रूप से सरणी आकार का ट्रैक रखने की आवश्यकता होगी। एक सामान्य तकनीक एक निश्चित आकार के विस्तार के बजाय, प्रत्येक बार विस्तारित सरणी आकार को दोगुना करना है; यह realloc पर कॉल की संख्या को कम करता है, जो संभावित रूप से महंगा है। बेशक इसका मतलब है कि आपको दो मात्राओं का ट्रैक रखना होगा; सरणी का कुल आकार और वर्तमान में पढ़ने वाले तत्वों की संख्या।

उदाहरण:

#define INITIAL_SIZE ... // some size large enough to cover most cases 

char **loadFile(FILE *stream, size_t *linesRead) 
{ 
    size_t arraySize = 0; 
    char **lines = NULL; 
    char *nextLine = NULL; 

    *linesRead = 0; 

    lines = malloc(INITIAL_SIZE * sizeof *lines); 
    if (!lines) 
    { 
    fprintf(stderr, "Could not allocate array\n"); 
    return NULL; 
    } 

    arraySize = INITIAL_SIZE; 

    /** 
    * Read the next input line from the stream. We're abstracting this 
    * out to keep the code simple. 
    */ 
    while ((nextLine = getNextLine(stream))) 
    { 
    if (arraySize <= *linesRead) 
    { 
     char **tmp = realloc(lines, arraysSize * 2 * sizeof *tmp); 
     if (tmp) 
     { 
     lines = tmp; 
     arraySize *= 2; 
     } 
    } 
    lines[(*linesRead)++] = nextLine; 
) 

    return lines; 
} 
संबंधित मुद्दे