Использование динамического выделения памяти для массивов

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

Например, вот следующий массив, в котором я читаю отдельные слова из файла.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, &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 (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 массивов:

http://www.eskimo.com/~scs/cclass/int/sx9b.html

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);
Другие вопросы по тегам