Dynamic Memory Allocation and You

char name[30];
When you write a declaration like the one above, memory (in this case 30 bytes) is allocated when the variable comes into existence (at beginning of the program's execution if it is a global variable, at the beginning of execution of a block of code if it is not). This is an acceptable way to do things if you know in advance how much memory you will need or are willing to waste memory by allocating more than you could ever need. But there will be times when you want to allocate memory, but you won't know how much until your program is running. In these situations, a technique called "dynamic memory allocation" is used. It is accomplished using the malloc() function in C or the new operator in C++.

The safecopy() function you saw on your exam used this type of memory allocation. To refresh your memory, here is the question.

Sometimes programmers need to make a copy of a string in permanent ("heap") memory. Write the function that does that. Its prototype should be: char *safecopy(char *s);. You give it a string as a parameter, it creates a new string using heap memory, copies the original into it, and returns the new string as its result. Why would a function like this be useful?
The question mentions heap memory, which is exactly what malloc() or new will give you. Clearly there is a need for dynamic memory allocation here. There is no way to know in advance how long the string that is passed to safecopy() will be.

Here is one possible implementation of safecopy().

char *safecopy(char *s)
{ char *t;
  t = new char[strlen(s)+1];
  strcpy(t, s);
  return t; }
That would only work in C++. If you have to use old C, you would write this instead:
char *safecopy(char *s)
{ char *t;
  t = (char *) malloc(strlen(s) + 1);
  if (t != NULL)
  { strcpy(t, s); }  
  return t; }
The reason for the +1 in the memory request is to account for the NUL (zero byte) that must go at the end of every string (but is not counted by strlen() -- read "Playing with Strings" for more on the intricacies and inner workings of the string functions). The check for NULL is necessary to avoid the segmentation fault that would occur if we tried to use the null pointer that malloc() gives us to say, "Out of memory." The C++ equivalent of the above call to malloc() is t = new char[strlen(s) + 1];.

The C library function strdup() does the same thing safecopy() does.

Keep in mind that the argument to malloc() is the number of bytes to allocate. In the sample implementation of safecopy() we use our knowledge of the fact that chars are only one byte to keep things simple. In cases where you don't know the size of data type you are allocating memory for, the sizeof operator is useful. For instance, the size of an int varies from computer to computer, and even from compiler to compiler on the same computer. So, to allocate memory for 10 integers in a portable manner, we should write malloc(10 * sizeof(int));. But beware sizeof's tricky nature.


Exercises

Split Revisited

The split() function that was part of an early homework assignment could have been written in several ways. Most of you wrote it to scan through the source string and call malloc() each time it saw a delimiter. It would then copy the part that immediately proceeded the delimiter into the newly allocated memory and add it to the parts array. Another way to do it was to make a safecopy of the whole input string right at the start, then scan through the safecopy of the string, changing delimiters to null zeros and putting pointers to the beginning of each part into the parts array. Whichever way you did it, try doing it the other way. (If you didn't do it either of these ways, try doing it both ways.)

The parts array was declared outside the split() in the version you did for homework. Thus, it had a fixed maximum size (as far as the function doing the splitting was concerned) and some memory was nearly always wasted. Write a version of the split function that uses dynamic memory allocation to make a parts array just big enough to hold the parts of the input string.



File Reversal

Write a program to read a text file and store its contents in an array of strings. (You might start by fixing the length of each line and the number of lines in the file, just to make sure you know how to read a file, but make a version that can store any number of lines of varying length to get practice using dynamic memory allocation.)

After you've loaded the file, print its lines in reverse order. [Solution]



Generating Anagrams

Write a function to generate anagrams for a given word. It would call some sort of dictionary lookup function to see if a permutation is a valid English word. The sample program below gives its prototype and demonstrates its usage.
int anagrams(char *word, char **anagrams); /* returns number of anagrams generated,
                                              puts list of anagrams in anagrams. */

void main(int argc, char *argv[])
{ char **a;
  int n;
  
  if (argc != 2)
  { printf("usage: %s word\n", argv[0]);
    exit(1); }

  n = anagrams(argv[1], &a);
  if (n == 0) printf("no anagrams.\n");
  while (n > 0)
  { n--;
    printf("%s\n", a[n]); } }
Note: The parameters of main() are there to get information about the command line used to run the program. The first, argc, is a count of the number of words on the command line (including the name of the program). The second, argv, is a vector (an array) of the words. So, if you called this program anagrams and you typed at the prompt anagrams liar, argc would be 2 and argv would contain the strings "anagrams" and "liar" (argv[0] and argv[1], respectively). The output of the program would then be something like
lair
liar 
rail
or, if you had a very incomplete dictionary,
no anagrams.

ast 4-Apr-2000