2011-01-09 2 views
10

Как я могу использовать распределение динамической памяти для массивов?Использование распределения динамической памяти для массивов

Для примера здесь является следующий массив, в котором я прочитал отдельные слова из файла .txt и сохранить их слово в слово в массиве:

Код:

char words[1000][15]; 

Здесь 1000 определяет количество слов, которые может сохранить массив, и каждое слово может содержать не более 15 символов.

Теперь я хочу, чтобы эта программа динамически выделяла память для количества слов, которое она считает. Например, файл .txt может содержать слова размером более 1000. Теперь я хочу, чтобы программа подсчитывала количество слов и соответствующим образом выделяла память.

Поскольку мы не можем использовать переменную вместо [1000], я совершенно не понимаю, как реализовать свою логику. Пожалуйста, помогите мне в этом.

ответ

17

Вы можете использовать указатели.

В частности, вы используете указатель на адрес и используя стандартные вызовы функций библиотеки c, вы просите операционную систему расширить кучу, чтобы вы могли хранить то, что вам нужно.

Теперь он может отказаться, с которым вам придется обращаться.

Следующий вопрос - как вы запрашиваете 2D-массив? Ну, вы запрашиваете массив указателей, а затем расширяете каждый указатель.

В качестве примера рассмотрит следующее:

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 */ 
} 

Это получает вас двумерный массив, где каждый элемент words[i] может иметь различный размер, определяемый во время выполнения, так же, как количество слов.

Вы должны free() все результирующей памяти с помощью цикла по массиву, когда вы закончите с этим:

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

free(words); 

Если вы этого не сделаете, вы будете создавать утечку памяти.

Вы также можете использовать calloc. Разница заключается в вызове конвенции и эффекте - calloc инициализирует всю память до 0, тогда как malloc - нет.

Если вам нужно изменить размер во время выполнения, используйте realloc.


Кроме того, важно, следить за word_size + 1, которые я использовал. Строки в C ноль-завершены, и это требует дополнительного символа, который вам нужно учитывать. Чтобы я помню это, я обычно устанавливаю размер переменной word_size независимо от размера слова (длина строки, как я ожидаю) и явно оставляю +1 в malloc для нуля. Тогда я знаю, что выделенный буфер может принимать строку из word_size символов. Не делать этого тоже хорошо - я просто делаю это, потому что я хочу явно объяснить нуль очевидным образом.

Существует также недостаток этого подхода - Я явно видел это как отправленную ошибку в последнее время. Заметьте, я написал (word_size+1)*sizeof(type) - представьте, однако, что я написал word_size*sizeof(type)+1. Для sizeof(type)=1 это одно и то же, но Windows использует wchar_t очень часто - и в этом случае вы зарезервируете один байт за свой последний нуль, а не на два, и они являются элементами с нулевым оканчиванием типа type, а не одиночными нулевыми байтами. Это означает, что вы будете набирать обороты при чтении и записи.  

Приложение: сделайте это независимо от того, что вам нравится, просто следите за этими нулевыми терминаторами, если вы собираетесь передать буфер тому, что полагается на них.

+0

Ваша терминология кажется немного запутанной здесь. Я ожидал бы, что num_words == 2 подразумевает, что должны быть два слова и слова [0], а слова [1] содержат их. Затем вы должны malloc (num_words * sizeof (char *)). –

+0

@ Вы правы. Я думаю, я имел в виду это в отношении +1 для учета нулевого терминатора. Исправление :) –

+0

Откуда возникает переменная 'num_words'? –

1

Если вы собираетесь использовать C++, STL очень полезен для динамического распределения и очень прост. Вы можете использовать станд :: вектор ..

+0

я не получил его. станд :: вектор ??? Я начинаю использовать C Программирование в Windows. Пожалуйста, объясните мне немного дальше. – Rafay

+0

Тогда забудьте о STL, если вы программируете на C. Следуйте по ссылке, предоставленной Джоном Бокером – 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

Вам нужно добавить дополнительную ячейку памяти в char, чтобы сохранить «\ 0» в конце. Не так ли? – Mahesh

7

В то время как Ninefingers предоставлены an answer using an array of pointers, вы также можете использовать массив массивов, если размер внутреннего массива является постоянным выражением. Код для этого проще.

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

Эта переменная 'num_words', которую я вижу везде, означает, что мы должны дать массиву длину и что мы просто выделяем память каждому элементу через malloc? Мы не динамически увеличиваем размер массива только по каждому элементу? –

1

Если 15 в вашем примере является переменным, используйте один из возможных ответов (от Девятипалого или Джона Бокер или Muggen). Если 1000 является переменной, используйте 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;} 
} 

В моем коде выше, константа 2000 является упрощение; Вы должны добавить еще одну переменную capacity для поддержки более 2000 слов:

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

В современном C (C99) у вас есть дополнительный выбор, массивы переменной длины, VLA, например, что:

char myWord[N]; 

В вы также можете сделать такую ​​вещь в двух измерениях, но если ваши размеры становятся слишком большими, вы можете рискнуть переполнением стека . В вашем случае проще всего будет использовать указатель на такой массив и использовать malloc/realloc изменять их размер:

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

Этот код должен работать (по модулю опечаток), если wordlen постоянная или переменная, до тех пор, пока вы держите все внутри одной функции.Если вы хотите разместить его в функцию, вы должны объявить функцию что-то вроде

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

, который параметры длины должны прийти первым, чтобы быть известным для объявления words.

1

Если вы работаете в 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; 
} 
Смежные вопросы