Lecture 8: 02 Feb, 2009


Khaled Harras (with a nod to Mark Stehlik)

Dynamically allocated Memory


Compile-time 2-D arrays of char vs. dynamic (run-time) arrays of pointers

In the last lecture, we stored our dictionary in a 2-dimensional array of chars similar to:

#define MAX_WORDS 10
#define MAX_WORD_LEN 30
int main()
{
    char wordArray[MAX_WORDS][MAX_WORD_LEN];
    /* assume only 5 words
    ("horses", "cats", "rats", "dogs", "bats")
    were read into the array
    */
    return 0;
}

The memory allocated for wordArray looked like this:  (the Ø symbol is the null character or '\0')

                0    1    2    3    4    5    6    7    8    9    0    1    2    3    4
wordArray[0]: ['h']['o']['r']['s']['e']['s'][ Ø ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ]
wordArray[1]: ['c']['a']['t']['s'][ Ø ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ]
wordArray[2]: ['r']['a']['t']['s'][ Ø ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ]
wordArray[3]: ['d']['o']['g']['s'][ Ø ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ]
wordArray[4]: ['b']['a']['t']['s'][ Ø ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ]
wordArray[5]: [ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ]
wordArray[6]: [ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ]
wordArray[7]: [ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ]
wordArray[8]: [ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ]
wordArray[9]: [ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ][ ? ]

As we saw in the last lecture, the disadvantages of the 2-D array of char are

  1. The amount of space allocated for each word is always MAX_WORD_LEN while the average word's actual length is likely much less. Thus, most of the storage is wasted.
  2. The number of rows in the array must be a compile-time constant. Thus,

A compile-time array of pointers solves disadvantage #1 but not #2

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_WORDS 10


int main(int argc, char *argv[])
{
    char *wordArray[MAX_WORDS];

    return 0;
}

The memory allocated for wordArray now looks like this

wordArray[  0      1      2      3      4      5      6      7      8      9  ]
          char*  char*  char*  char*  char*  char*  char*  char*  char*  char*
            |      |      |      |      |
        "horses"   |      |      |      |
                 "cats"   |      |    "bats"
                       "rats"    |
                               "dogs"

The disadvantage still remains that the number of pointers allocated is a compile time constant that may be much smaller or larger than the actual size of the input. So, like before,


The dynamic array of pointers solves both disadvantages!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_WORD_LEN 30

int main(int argc, char *argv[])
{
	char word[MAX_WORDLEN];  /* no getting around this! */
	char **wordArray;  /* pointer to char* - we will malloc an array of
	char*'s to this ptr at runtime */ 

	return 0;
}

The advantage to the dynamic array is obvious. We can allocate it at runtime choosing a reasonable number of initial words. If we assume an input file as follows:

horses
cats
rats
dogs
bats

The memory allocated for wordArray now looks like this

wordArray[  0      1      2      3      4  ]
          char*  char*  char*  char*  char*
            |      |      |      |      |
        "horses"   |      |      |      |
                 "cats"   |      |   "bats"
                       "rats"    |
                               "dogs"

FYI

Lab 3 similarly requires that you initialize your array to some default initial value (50). Then, if your array fills to capacity, you must resize your array by doubling its capacity, copying the old pointers into the first half of new array and then freeing the old array.


Answers to Pop Quizzes:

  1. No, because the run-time environment will clean that sort of stuff up. You definitely should free any run-time allocated memory that you're no longer using, though, while the program is running.

  2. If we delete the array of pointers first, the variable wordArray becomes stale and we need it to access the individual pointers in the array. So, doing it in reverse causes you to reference stale memory when you try to free the strings. Dynamic data structures must be freed from the inside out!