2011-01-09 55 views
10

Làm cách nào để sử dụng phân bổ bộ nhớ động cho mảng?Sử dụng phân bổ bộ nhớ động cho các mảng

Ví dụ ở đây là mảng sau đây trong đó tôi đọc từ riêng lẻ từ một tập tin .txt và lưu chúng từng chữ trong mảng:

Code:

char words[1000][15]; 

đây 1000 xác định số các từ mà mảng có thể lưu và mỗi từ có thể bao gồm không quá 15 ký tự.

Bây giờ tôi muốn rằng chương trình đó sẽ tự động cấp phát bộ nhớ cho số từ mà nó đếm. Ví dụ, một tập tin .txt có thể chứa các từ lớn hơn 1000. Bây giờ tôi muốn chương trình nên đếm số lượng từ và phân bổ bộ nhớ cho phù hợp.

Vì chúng tôi không thể sử dụng biến thay vì [1000], tôi hoàn toàn trống khi thực hiện logic của mình. Xin hãy giúp tôi trong vấn đề này.

Trả lời

17

Bạn sử dụng con trỏ.

Cụ thể, bạn sử dụng con trỏ đến địa chỉ và sử dụng cuộc gọi hàm thư viện chuẩn c, bạn yêu cầu hệ điều hành mở rộng vùng lưu trữ để cho phép bạn lưu trữ những gì bạn cần.

Bây giờ, nó có thể từ chối, mà bạn sẽ cần phải xử lý.

Câu hỏi tiếp theo sẽ trở thành - làm cách nào bạn yêu cầu một mảng 2D? Vâng, bạn yêu cầu một mảng con trỏ, và sau đó mở rộng mỗi con trỏ.

Như một ví dụ, hãy xem xét điều này:

int i = 0; 
char** words; 
words = malloc((num_words)*sizeof(char*)); 

if (words == NULL) 
{ 
    /* we have a problem */ 
    printf("Error: out of memory.\n"); 
    return; 
} 

for (i=0; i<num_words; i++) 
{ 
    words[i] = malloc((word_size+1)*sizeof(char)); 
    if (words[i] == NULL) 
    { 
     /* problem */ 
     break; 
    } 
} 

if (i != num_words) 
{ 
    /* it didn't allocate */ 
} 

này sẽ cho bạn một mảng hai chiều, trong đó mỗi yếu tố words[i] có thể có kích thước khác nhau, xác định được tại thời gian chạy, cũng như số lượng từ là.

Bạn sẽ cần phải free() tất cả các bộ nhớ kết quả bằng cách lặp qua mảng khi bạn đang thực hiện với nó:

for (i = 0; i < num_words; i++) 
{ 
    free(words[i]); 
} 

free(words); 

Nếu bạn không, bạn sẽ tạo một rò rỉ bộ nhớ.

Bạn cũng có thể sử dụng calloc. Sự khác biệt là khi gọi quy ước và hiệu lực - calloc khởi tạo tất cả bộ nhớ thành 0 trong khi malloc thì không.

Nếu bạn cần đổi kích thước khi chạy, hãy sử dụng realloc.


Ngoài ra, quan trọng, xem ra cho word_size + 1 mà tôi đã sử dụng. Các chuỗi trong C là zero-terminated và điều này cần thêm một ký tự mà bạn cần tính đến. Để đảm bảo tôi nhớ điều này, tôi thường đặt kích thước của biến số word_size thành bất kỳ kích thước nào của từ phải là (độ dài của chuỗi như tôi mong đợi) và rõ ràng để lại +1 trong malloc cho số không. Sau đó, tôi biết rằng bộ đệm được phân bổ có thể lấy một chuỗi gồm word_size ký tự. Không làm điều này cũng tốt - tôi chỉ làm điều đó bởi vì tôi muốn giải thích rõ ràng số không theo một cách rõ ràng.

Ngoài ra còn có một nhược điểm của phương pháp này - Tôi đã thấy rõ ràng đây là lỗi được vận chuyển gần đây. Chú ý tôi đã viết (word_size+1)*sizeof(type) - tưởng tượng tuy nhiên tôi đã viết word_size*sizeof(type)+1. Đối với sizeof(type)=1 đây là những điều tương tự nhưng Windows sử dụng wchar_t rất thường xuyên - và trong trường hợp này, bạn sẽ đặt trước một byte cho số không cuối cùng của bạn thay vì hai - và chúng là các phần tử không được kết thúc loại type, chứ không phải một byte không. Điều này có nghĩa là bạn sẽ tràn ngập đọc và viết.  

Phụ lục: thực hiện theo cách bạn muốn, chỉ xem ra cho những người không có terminators nếu bạn định chuyển bộ đệm cho thứ gì đó dựa vào chúng.

+0

Thuật ngữ của bạn có vẻ hơi khó hiểu ở đây. Tôi hy vọng num_words == 2 ngụ ý rằng cần có hai từ và từ [0] và các từ [1] chứa chúng. Sau đó bạn nên malloc (num_words * sizeof (char *)). –

+0

@Bạn nói đúng. Tôi nghĩ rằng tôi có nghĩa là nó liên quan đến +1 để giải thích cho người kết thúc bằng không. Sửa lỗi :) –

+0

Biến 'num_words' đến từ đâu? –

1

Nếu bạn định sử dụng C++, STL rất hữu ích cho việc phân bổ động và rất dễ dàng. Bạn có thể sử dụng std :: vector ..

+0

tôi đã không nhận được nó. std :: vector ??? Tôi là người mới bắt đầu sử dụng Lập trình C trong Windows. Vui lòng giải thích cho tôi thêm một chút. – Rafay

+0

Sau đó, quên STL nếu bạn đang lập trình C. Theo liên kết được đưa ra bởi John Boker – Mahesh

0
char ** words = malloc(1000 * sizeof(char *)); 
int i; 
for(i = 0 ; i < 1000 ; i++) 
    *(words+i) = malloc(sizeof(char) * 15); 

//.... 
for(i = 0 ; i < 1000 ; i++) 
    free(*(words+i)); 

free(words); 
+1

Bạn cần thêm một vị trí bộ nhớ bổ sung vào char để giữ '\ 0' ở cuối. Phải không? – Mahesh

7

Trong khi Ninefingers cung cấp an answer using an array of pointers, bạn cũng có thể sử dụng một mảng các mảng miễn là kích thước của mảng bên trong là biểu thức không đổi. Mã cho việc này đơn giản hơn.

char (*words)[15]; // 'words' is pointer to char[15] 
words = malloc (num_words * sizeof(char[15]); 

// to access character i of word w 
words[w][i]; 

free(words); 
+0

Biến 'num_words' tôi thấy ở mọi nơi có nghĩa là chúng ta phải cung cấp cho mảng một độ dài và chúng ta chỉ đơn giản là cấp phát bộ nhớ cho mỗi phần tử thông qua malloc? Chúng tôi không tự động làm cho kích thước của mảng lớn hơn chỉ mỗi phần tử? –

1

Nếu 15 trong ví dụ của bạn có thể thay đổi, sử dụng một trong các câu trả lời có sẵn (từ Ninefingers hay John Boker hoặc Muggen). Nếu 1000 là biến, sử dụng realloc:

words = malloc(1000 * sizeof(char*)); 
// ... read 1000 words 
if (++num_words > 1000) 
{ 
    char** more_words = realloc(words, 2000 * sizeof(char*)); 
    if (more_words) {printf("Too bad");} 
    else {words = more_words;} 
} 

Trong mã của tôi ở trên, hằng 2000 là một việc đơn giản hóa; bạn nên thêm một biến khác capacity để hỗ trợ hơn 2000 chữ:

if (++num_words > capacity) 
{ 
    // ... realloc 
    ++capacity; // will reallocate 1000+ words each time; will be very slow 
    // capacity += 1000; // less reallocations, some memory wasted 
    // capacity *= 2; // less reallocations but more memory wasted 
} 
0

Trong C hiện đại (C99), bạn có một sự lựa chọn bổ sung, mảng chiều dài thay đổi, VLA, chẳng hạn như:

char myWord[N]; 

Trong nguyên tắc bạn cũng có thể làm một điều như vậy trong hai chiều, nhưng nếu kích thước của bạn quá lớn, bạn có thể gặp nguy cơ tràn ngăn xếp . Trong trường hợp của bạn là điều đơn giản nhất là nên sử dụng một con trỏ đến một mảng như thế và sử dụng malloc/realloc để thay đổi kích cỡ chúng:

typedef char Word[wordlen]; 
size_t m = 100000; 

Word* words = malloc(m * sizeof(Word)); 
/* initialize words[0]... words[m-1] here */ 
for (size_t i = 0; i < m; ++i) words[i][0] = '\0'; 

/* array is too small? */ 
m *= 2; 
void *p = realloc(words, m*sizeof(Word)); 
if (p) words = p; 
else { 
/* error handling */ 
} 
. 
free(words); 

Mã này nên làm việc (modulo lỗi chính tả) nếu wordlen là một hằng số hoặc một biến, miễn là bạn giữ mọi thứ bên trong một chức năng.Nếu bạn muốn đặt nó trong một hàm, bạn nên khai báo chức năng một cái gì đó của bạn như

void myWordFunc(size_t wordlen, size_t m, char words[m][wordlen]); 

đó là các thông số chiều dài phải đến đầu tiên được biết đến với việc kê khai của words.

1

Nếu bạn đang làm việc trong C:

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

#define WORD_LEN 15 

int resizeArray(char (**wordList)[WORD_LEN], size_t *currentSize, size_t extent) 
{ 
    int result = 1; 
    char (*tmp)[WORD_LEN] = realloc(*wordList, 
           (*currentSize + extent) * sizeof **wordList); 
    if (tmp) 
    { 
    *currentSize += extent; 
    *wordList = tmp; 
    } 
    else 
    result = 0; 

    return result; 
} 

int main(void) 
{ 
    char *data[] = {"This", "is", "a", "test", 
        "of", "the", "Emergency", 
        "Broadcast", "System", NULL}; 
    size_t i = 0, j; 
    char (*words)[WORD_LEN] = NULL; 
    size_t currentSize = 0; 

    for (i = 0; data[i] != NULL; i++) 
    { 
    if (currentSize <= i) 
    { 
     if (!resizeArray(&words, &currentSize, 5)) 
     { 
     fprintf(stderr, "Could not resize words\n"); 
     break; 
     } 
    } 
    strcpy(words[i], data[i]); 
    } 

    printf("current array size: %lu\n", (unsigned long) currentSize); 
    printf("copied %lu words\n", (unsigned long) i); 

    for (j = 0; j < i; j++) 
    { 
    printf("wordlist[%lu] = \"%s\"\n", (unsigned long) j, words[j]); 
    } 

    free(words); 

    return 0; 
} 
Các vấn đề liên quan