14

How am I supposed to use dynamic memory allocations for arrays?

For example here is the following array in which i read individual words from a .txt file and save them word by word in the array:

Code:

char words[1000][15];

Here 1000 defines the number of words the array can save and each word may comprise of not more than 15 characters.

Now I want that that program should dynamically allocate the memory for the number of words it counts. For example, a .txt file may contain words greater that 1000. Now I want that the program should count the number of words and allocate the memory accordingly.

Since we cannot use a variable in place of [1000], I am completely blank at how to implement my logic. Please help me in this regard.

jmj
  • 237,923
  • 42
  • 401
  • 438
Rafay
  • 6,108
  • 11
  • 51
  • 71

8 Answers8

27

You use pointers.

Specifically, you use a pointer to an address, and using a standard c library function calls, you ask the operating system to expand the heap to allow you to store what you need to.

Now, it might refuse, which you will need to handle.

The next question becomes - how do you ask for a 2D array? Well, you ask for an array of pointers, and then expand each pointer.

As an example, consider this:

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

This gets you a two-dimensional array, where each element words[i] can have a different size, determinable at run time, just as the number of words is.

You will need to free() all of the resultant memory by looping over the array when you're done with it:

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

free(words);

If you don't, you'll create a memory leak.

You could also use calloc. The difference is in calling convention and effect - calloc initialises all the memory to 0 whereas malloc does not.

If you need to resize at runtime, use realloc.


Also, important, watch out for the word_size+1 that I have used. Strings in C are zero-terminated and this takes an extra character which you need to account for. To ensure I remember this, I usually set the size of the variable word_size to whatever the size of the word should be (the length of the string as I expect) and explicitly leave the +1 in the malloc for the zero. Then I know that the allocated buffer can take a string of word_size characters. Not doing this is also fine - I just do it because I like to explicitly account for the zero in an obvious way.

There is also a downside to this approach - I've explicitly seen this as a shipped bug recently. Notice I wrote (word_size+1)*sizeof(type) - imagine however that I had written word_size*sizeof(type)+1. For sizeof(type)=1 these are the same thing but Windows uses wchar_t very frequently - and in this case you'll reserve one byte for your last zero rather than two - and they are zero-terminated elements of type type, not single zero bytes. This means you'll overrun on read and write.  

Addendum: do it whichever way you like, just watch out for those zero terminators if you're going to pass the buffer to something that relies on them.

Sam Brightman
  • 2,831
  • 4
  • 36
  • 38
  • Your terminology seems a little confusing here. I would expect num_words == 2 implies that there should be two words and words[0] and words[1] contain them. You should then malloc(num_words * sizeof(char*)). – Sam Brightman Mar 31 '15 at 12:37
  • @Sam you're right. I think I meant it with regards to the +1 to account for the zero terminator. Fixing :) –  Mar 31 '15 at 13:14
  • Where does the `num_words` variable come from? – atw Dec 01 '16 at 20:13
7

While Ninefingers provided an answer using an array of pointers , you can also use an array of arrays as long as the inner array's size is a constant expression. The code for this is simpler.

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);
Community
  • 1
  • 1
Heatsink
  • 7,721
  • 1
  • 25
  • 36
  • Does this `num_words` variable I see everywhere mean that we have to give the array a length and that we are simply allocating memory to each element via malloc? We are not dynamically making the size of the array bigger just each element? – atw Dec 01 '16 at 20:30
2

If you're working in 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;
}
John Bode
  • 119,563
  • 19
  • 122
  • 198
1

If you intend to go for C++, STL is very useful for something dynamic allocation and is very easy. You can use std::vector ..

Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • I didn't get it. std::vector??? I am a beginner using C Programming in Windows. Kindly explain me a bit further. – Rafay Jan 09 '11 at 20:15
  • Then forget about STL if you are programming in C. Follow the link given by John Boker – Mahesh Jan 09 '11 at 20:16
1

If the 15 in your example is variable, use one of the available answers (from Ninefingers or John Boker or Muggen). If the 1000 is variable, use 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;}
}

In my code above, the constant 2000 is a simplification; you should add another variable capacity to support more than 2000 words:

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
}
anatolyg
  • 26,506
  • 9
  • 60
  • 134
1

In modern C (C99) you have an additional choice, variable length arrays, VLA, such as that:

char myWord[N];

In principle you could also do such a thing in two dimensions, but if your sizes get too big, you may risk a stack overflow. In your case the easiest thing would be to use a pointer to such an array and to use malloc / realloc to resize them:

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

This code should work (modulo typos) if wordlen is a constant or a variable, as long as you keep everything inside one function. If you want to place it in a function you should declare your function something like

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

that is the length parameters must come first to be known for the declaration of words.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
0

Here is a little information on dynamically allocating 2d arrays:

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

John Boker
  • 82,559
  • 17
  • 97
  • 130
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);