Использование динамического выделения памяти для массивов
Как я должен использовать динамическое распределение памяти для массивов?
Например, вот следующий массив, в котором я читаю отдельные слова из файла.txt и сохраняю их слово в слово в массиве:
Код:
char words[1000][15];
Здесь 1000 определяет количество слов, которые массив может сохранить, и каждое слово может содержать не более 15 символов.
Теперь я хочу, чтобы эта программа динамически распределяла память по количеству слов, которые она считает. Например, файл.txt может содержать слова больше 1000. Теперь я хочу, чтобы программа посчитала количество слов и соответственно распределила память.
Поскольку мы не можем использовать переменную вместо [1000], я совершенно не знаю, как реализовать мою логику. Пожалуйста, помогите мне в этом отношении.
8 ответов
Вы используете указатели.
В частности, вы используете указатель на адрес, и, используя стандартные вызовы функций библиотеки 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
, не один ноль байтов. Это означает, что вы переполните чтение и запись.
Приложение: делайте это так, как вам нравится, просто следите за этими нулевыми терминаторами, если вы собираетесь передать буфер чему-то, что полагается на них.
Хотя Ninefingers предоставили ответ, используя массив указателей, вы также можете использовать массив массивов, если размер внутреннего массива является постоянным выражением. Код для этого проще.
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);
Если вы работаете в 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, ¤tSize, 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 (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
,
Если вы собираетесь использовать C++, STL очень полезен для динамического размещения и очень прост. Вы можете использовать std::vector ..
Если 15
в вашем примере переменная, используйте один из доступных ответов (от Ninefingers или Джона Бокера или 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
}
Вот небольшая информация о динамическом размещении 2d массивов:
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);