Использование динамического выделения памяти для массивов - PullRequest
12 голосов
/ 09 января 2011

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

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

Код:

char words[1000][15];

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

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

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

Ответы [ 8 ]

23 голосов
/ 09 января 2011

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

В частности, вы используете указатель на адрес, и, используя стандартные вызовы функций библиотеки 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, а не один ноль байтов. Это означает, что вы переполните чтение и запись.

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

7 голосов
/ 09 января 2011

Хотя 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);
1 голос
/ 10 января 2011

Если вы работаете в 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;
}
1 голос
/ 09 января 2011

Если 15 в вашем примере является переменной, используйте один из доступных ответов (от Ninefingers или John Boker или 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
}
1 голос
/ 09 января 2011

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

0 голосов
/ 10 января 2011

В современном 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.

0 голосов
/ 09 января 2011
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);
0 голосов
/ 09 января 2011

Вот небольшая информация о динамическом размещении 2d массивов:

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

...