2011-08-28 58 views
9

Tôi đã quen với PHP, nhưng tôi bắt đầu tìm hiểu C. Tôi đang cố gắng tạo một chương trình đọc một dòng tệp theo từng dòng và lưu trữ từng dòng vào một mảng.Cách tạo mảng có kích thước động trong C?

Cho đến nay tôi có một chương trình đọc dòng tệp theo từng dòng và thậm chí in từng dòng khi nó đi, nhưng bây giờ tôi chỉ cần thêm mỗi dòng vào một mảng.

Bạn thân tối qua đã kể cho tôi một chút về điều đó. Anh ta nói tôi sẽ phải sử dụng một mảng đa chiều trong C, vì vậy về cơ bản là array[x][y]. Phần [y] chính nó là dễ dàng, bởi vì tôi biết số lượng byte tối đa mà mỗi dòng sẽ được. Tuy nhiên, tôi không biết có bao nhiêu dòng dòng tệp sẽ.

Tôi hình tôi có thể làm cho nó lặp qua tệp và chỉ tăng số nguyên mỗi lần và sử dụng nó, nhưng tôi cảm thấy rằng có thể có một cách đơn giản hơn để làm điều đó.

Bất kỳ ý tưởng hoặc thậm chí một gợi ý đúng hướng? Tôi đánh giá cao sự giúp đỡ nào.

+0

Bạn có thể sử dụng chức năng 'realloc' để thay đổi kích thước của mảng sau này. – Jonathon

+0

Tôi sẽ tìm kiếm chức năng đó và cố gắng nghĩ về cách tôi có thể thực hiện nó và tôi sẽ liên hệ lại với bạn, cảm ơn – Rob

Trả lời

9

Để tự động phân bổ một mảng 2D:

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); 

Sửa đổi các phương pháp trên. Khi bạn cần một dòng khác để được thêm, hãy làm *(p + i) = malloc (sizeof (char) * dim2); và cập nhật i. Trong trường hợp này, bạn cần phải dự đoán số dòng tối đa trong tệp được chỉ định bằng biến số dim1, lần đầu tiên chúng tôi phân bổ mảng p. Điều này sẽ chỉ phân bổ các (sizeof (int *) * dim1) byte, do đó lựa chọn tốt hơn nhiều so với char p[dim1][dim2] (trong c99).

Có một cách khác tôi nghĩ. Phân bổ mảng trong khối và chuỗi chúng khi có tràn.

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; 

Sau khi khối đầu tiên sẵn sàng sử dụng. Khi bạn cần phải chèn một dòng chỉ làm:

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

Khi n được LINE_MAX phân bổ khối khác và liên kết nó với trang này.

struct _lines *temp; 

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

Điều gì đó tương tự.

Khi một khối n trở thành 0, hãy deallocate nó và cập nhật con trỏ khối hiện tại file thành hình trước đó. Bạn có thể đi qua từ đầu danh sách liên kết duy nhất và đi qua từ đầu hoặc sử dụng liên kết đôi.

+0

Cách tiếp cận thứ hai là cách hoạt động của 'đuôi (1)' của GNU hoạt động, tôi tin rằng, khi tệp đầu vào không tìm kiếm được (chẳng hạn như một đường ống hoặc stdin). Vì nó chỉ có thể thực hiện một lần thông qua tệp đầu vào trong những trường hợp đó, nó lưu trữ tệp trong một danh sách liên kết của các blob bộ nhớ và khi nó chạm vào cuối tệp, nó tìm kiếm ngược để in ra các dòng 'N' cuối cùng. –

+0

@Adam Rosenfield: Không biết điều đó, cảm ơn thông tin. Tôi đã sử dụng điều này để lưu trữ một danh sách dài các từ trong bộ nhớ từ lâu, và nó khá hữu ích. – phoxis

1

Nếu bạn đang sử dụng C, bạn sẽ cần tự triển khai kích thước của mảng đó. C++ và SDL đã thực hiện điều này cho bạn. Nó được gọi là vector. http://www.cplusplus.com/reference/stl/vector/

+0

Thật không may, tôi đang cố gắng gắn bó với C, nhưng cảm ơn. – Rob

5

Không có loại mảng có thể thay đổi kích thước chuẩn trong C. Bạn phải tự mình triển khai hoặc sử dụng thư viện của bên thứ ba. Dưới đây là một trần xương đơn giản ví dụ:

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++; 
} 

Sử dụng nó như thế này:

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); 

Tất nhiên, đây chỉ là một mảng của int s.Vì C không có mẫu, bạn phải đặt tất cả mã này vào macro cho mỗi loại mảng khác nhau (hoặc sử dụng một bộ tiền xử lý khác như GNU m4). Hoặc, bạn có thể sử dụng vùng chứa mảng chung hoặc sử dụng các con trỏ void* (yêu cầu tất cả các phần tử mảng phải là các đốm bộ nhớ mờ hoặc mờ) sẽ yêu cầu một phép đúc với mọi truy cập phần tử và memcpy cho mọi phần tử nhận/đặt.

Trong mọi trường hợp, nó không đẹp. Mảng hai chiều thậm chí còn xấu hơn.

0

Thay vì mảng ở đây, bạn cũng có thể sử dụng danh sách được liên kết, Mã đơn giản hơn, nhưng việc phân bổ thường xuyên hơn và có thể bị phân mảnh.

Miễn là bạn không có kế hoạch thực hiện nhiều truy cập ngẫu nhiên (Đó là O (n) tại đây), việc lặp lại đơn giản như một mảng thông thường.

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

Trong khi mảng đa chiều có thể giải quyết vấn đề này, một mảng 2D hình chữ nhật sẽ không thực sự là giải pháp C tự nhiên.

Đây là chương trình ban đầu đọc tệp vào danh sách được liên kết và sau đó phân bổ vectơ con trỏ có kích thước phù hợp. Mỗi ký tự riêng lẻ sau đó xuất hiện dưới dạng array[line][col] nhưng trên thực tế mỗi hàng chỉ miễn là cần. Đó là C99 ngoại trừ <err.h>.

#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

Bạn có thể sử dụng các chức năng mallocrealloc để tự động phân bổ và thay đổi kích thước một mảng của các con trỏ tới char, và mỗi phần tử của mảng sẽ trỏ đến một chuỗi đọc từ tập tin (nơi lưu trữ của chuỗi cũng được phân bổ động). Để đơn giản, chúng tôi giả định rằng độ dài tối đa của mỗi dòng nhỏ hơn M ký tự (đếm dòng mới), vì vậy chúng tôi không phải thực hiện bất kỳ thay đổi kích thước động nào của từng dòng.

Bạn sẽ cần phải theo dõi kích thước mảng theo cách thủ công mỗi lần bạn mở rộng. Một kỹ thuật phổ biến là tăng gấp đôi kích thước mảng mỗi khi bạn mở rộng, thay vì mở rộng bằng một kích thước cố định; điều này giảm thiểu số lượng cuộc gọi đến realloc, có khả năng tốn kém. Tất nhiên điều đó có nghĩa là bạn sẽ phải theo dõi hai số lượng; tổng kích thước của mảng và số phần tử hiện đang đọc.

Ví dụ:

#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; 
} 
Các vấn đề liên quan