Memory Allocation
Space is automatically set aside for variables when they are defined, but sometimes you don't know beforehand how many variables you'll need or just how long an array might need to be. The malloc command creates space, returning a pointer to this new area. To illustrate its use and dangers, here's a sequence of attempts at writing a string reverser program.
#include <stdio.h> #include <stdlib.h> void print_reverse(char *str) { int i; unsigned int len; len = strlen(str) - 1; /* Why the -1? Because arrays start at 0, so if a string has n chars, the last char will be at position n-1 */ for (i=len; i>=0; i--) putchar(str[i]); } int main() { char input_str[100] /* why 100? */ printf("Input a string\n"); gets(input_str); /* should check return value */ printf("String was %s\n", input_str); print_reverse(input_str); }
This works, but is a bit `loose' (suppose the user types more than 100 characters?) and doesn't keep a copy of the reversed string should it be needed later. The next example shows a wrong (but not uncommon) attempt to solve the latter limitation.
#include <stdio.h> /* WRONG! */ char* make_reverse(char *str) { int i, j; unsigned int len; char newstr[100]; len = strlen(str) - 1; j=0; for (i=len; i>=0; i--;) newstr[j] = str[i]; j++; /* now return a pointer to this new string */ return newstr; } int main() { char input_str[100]; /* why 100? */ char *c_ptr; printf("Input a string\n"); gets(input_str); /* should check return value */ c_ptr = make_reverse(input_str); printf("String was %s\n", input_str); printf("Reversed string is %s\n", c_ptr); }
Like many flawed C programs this will work much of the time, especially if it's not part of a bigger program. The problems are that :-
- The memory allocated for newstr when it was declared as an
`automatic' variable in make_reverse isn't permanent - it only lasts
as long as make_reverse() takes to execute. However, the
array's contents aren't erased, they're just freed for later use, so if you
access the array from main you might still get away
with it for a while. Making newstr a static will preserve the
data but only until it's overwritten by a subsequent call.
- The newly created array of characters, newstr, isn't terminated
with a zero character,
`\0'
, so trying to print the characters out as a string may be disastrous. `Luckily' the memory location that should have been set to zero is likely to be zero anyway.
Let's try again.
/* mallocing.c */ #include <stdio.h> #include <stdlib.h> char* make_reverse(char *str) { int i; unsigned int len; char *ret_str, *c_ptr; len = strlen(str); /* Create enough space for the string AND the final \0. */ ret_str = (char*) malloc(len +1); /* Now ret_str points to a `permanent' area of memory. */ /* Point c_ptr to where the final '\0' goes and put it in */ c_ptr = ret_str + len; *c_ptr = '\0'; /* now copy characters from str into the newly created space. The str pointer will be advanced a char at a time, the cptr pointer will be decremented a char at a time. */ while(*str !=0){ /* while str isn't pointing to the last '\0' */ c_ptr--; *c_ptr = *str; str++; /* increment the pointer so that it points to each character in turn. */ } return ret_str; } int main() { char input_str[100]; /* why 100? */ char *c_ptr; printf("Input a string\n"); gets(input_str); /* Should check return value */ c_ptr = make_reverse(input_str); printf("String was %s\n", input_str); printf("Reversed string is %s\n", c_ptr); }
The malloc'ed space will be preserved until it is explicitly freed (in this case by doing `free(c_ptr)'). Note that the pointer to the malloc'ed space is the only way you have to access that memory: lose it and the memory will be inaccessible. It will only be freed when the program finishes.
malloc is often used to create tree and list structures, since one often doesn't know beforehand how many items will be needed. See section A.4 for an example.