Figure %: ptr = malloc(1024);

Normally, however, we wouldn't allocate some random number of bytes; we would want to allocate enough space to hold some specific data, some number of variables. As such, a commonly used operator is the sizeof() operator. sizeof() takes a type as an argument and gives back the size, in bytes, of that type. It is often used in conjunction with malloc to allocate enough space to hold a single variable or array of variables. The sizeof() operator is particularly useful for programs that must run under more than one operating system as the length in bytes of a data type may not be the same on different systems.

Some examples:

To allocate:We use:
An integermalloc(sizeof(int));
An unsigned charactermalloc(sizeof(unsigned char));
An array of 21 longsmalloc(21 * sizeof(long));
An array of 10 pointers to integersmalloc(10 * sizeof(int *));

So how can we use this in real code? Here's an example. Remember our unhappy professor? We can easily change our array grades program such that the size of the array can be set at run-time (meaning while actually running the program as opposed to at compile time).

int main() { int i=0; int *grades; int size; printf("Enter the number of students:\n"); scanf("%d\n", &size); grades = malloc(size * sizeof(int)); do { printf("Enter grade #%d:\n", i+1); scanf("%d\n", &grades[i]); i++; } while(i<size); }
So what does this do? It reads in the size of the array to create into the variable size. It then uses malloc to allocate enough memory to hold that many integers. As the memory it allocates will be continuous, we can use this memory just like an array. We store the address of that memory into grades and the rest program is basically as it was above.

There are still a few key elements missing here. The first, a very important part of programming, is error detection. Remember that if we try to dereference a NULL pointer, it will very often cause something bad to happen, like making our program crash. If for some reason malloc() cannot allocate memory, it will return NULL. So there exists the possibility that if malloc() cannot allocate the requested memory, the value of NULL will be stored in grades, and then when we try to access the ith element of grades, we will have a problem. To prevent problems like these, we need to check to see if the result of the call to malloc() returns NULL. If it does, there was an error, and we need to handle it. How you handle it depends on how you're using the memory, but in this case we'll just display an error and exit the program.

int main() { int i=0; int *grades; int size; printf("Enter the number of students:\n"); scanf("%d\n", &size); if ((grades = malloc(size * sizeof(int)) == NULL) { printf("Error: Unable to allocate memory for array\n"); exit(1); } do { printf("Enter grade #%d:\n", i+1); scanf("%d\n", &grade[i]); i++ } while(i<size); }

The second key element missing here deals with giving back this memory we've allocated when we're done using it.

free()

Thus far we've only discussed allocating memory. When your program requests memory and the operating system gives it, the operating system marks that memory as "in use" and will not allow any other application to use it (in fact, if another application does attempt to use it, the operating system will most likely try to kill that program; remember what happens when we try to dereference a pointer that doesn't point to memory we own). If your program never frees up the memory it requested once it is done using it, no one else will be able to use it. So, when we're done using memory we've requested, we need to give it back so other programs can use it. It's that easy.

To free memory in C we use the function free(). The free() function takes one argument, a pointer to the memory we want to free. This memory must have been previously allocated with free()'s counterpart, malloc(). For example, if we have an integer pointer int *steve and if steve points to some memory your program previously requested, to free it, all we have to do is make the call free(steve). Easy enough. There are, however, a few things to be careful of when using free():

  • Don't free() memory twice. When you free memory, you relinquish your rights to it. After you've free'd memory once, it is no longer yours. If you try to free it again, what you're really trying to do is free memory that you don't own; it doesn't matter that you once owned it, you don't anymore. So, free()ing memory twice is like coding your program with the explicit instruction to crash.
  • Don't free() static memory, as in:
    int arr[100]; free(arr); /* bad! */
  • Don't free anywhere but the beginning of a chunk of memory you've allocated. For example, if you allocate a block of memory and store the address into a variable int *steve, don't do something like free(steve + 5). This will result in the computer attempting to free up the memory at steve + 5 which is not the exact address previously returned by the operating system. This most likely won't crash your program, but it might result in strange behavior.