So far, your programs have used one kind of memory – frames on the stack. Recall that every function has a frame where its local variables are stored. This memory is automatically allocated when a function starts and automatically deallocated when the function ends. In fact, local variables are sometimes called automatic variables because of this convenient behavior.
Sometimes, however, you need to claim a contiguous chunk of memory yourself – a buffer. Programmers often use the word buffer to mean a long line of bytes of memory. The buffer comes from a region of memory known as the heap, which is separate from the stack.
On the heap, the buffer is independent of any function’s frame. Thus, it can be used across many functions. For example, you could claim a buffer of memory intended to hold some text. You could then call a function that would read a text file into the buffer, call a second function that would count all the vowels in the text, and call a third function to spellcheck it. When you were finished using the text, you would return the memory that was in the buffer to the heap.
You request a buffer of memory using the function malloc(). When you are done using the buffer, you call the function free() to release your claim on that memory and return it to the heap.
Let’s say, for example, you needed a chunk of memory big enough to hold 1,000 floats. Note the crucial use of sizeof() to get the right number of bytes for your buffer.
#include <stdio.h> #include <stdlib.h> // malloc() and free() are in stdlib.h int main(int argc, const char * argv[]) { // Declare a pointer float *startOfBuffer; // Ask to use some bytes from the heap startOfBuffer = malloc(1000 * sizeof(float)); // ...use the buffer here... // Relinquish your claim on the memory so it can be reused free(startOfBuffer); // Forget where that memory is startOfBuffer = NULL; return 0; }
startOfBuffer is a pointer to the address of the first floating point number in the buffer.
At this point, most C books would spend a lot of time talking about how to use startOfBuffer to read and write data in different locations in the buffer of floating pointer numbers. This book, however, is trying to get you to objects as quickly as possible. So, we will put off these concepts until later.
In Chapter 11, you created a struct as a local variable in main()’s frame on the stack. You can also allocate a buffer on the heap for a struct. To create a Person struct on the heap, you could write a program like this:
#include <stdio.h> #include <stdlib.h> typedef struct { float heightInMeters; int weightInKilos; } Person; float bodyMassIndex(Person *p) { return p->weightInKilos / (p->heightInMeters * p->heightInMeters); } int main(int argc, const char * argv[]) { // Allocate memory for one Person struct Person *mikey = (Person *)malloc(sizeof(Person)); // Fill in two members of the struct mikey->weightInKilos = 96; mikey->heightInMeters = 1.7; // Print out the BMI of the original Person float mikeyBMI = bodyMassIndex(mikey); printf("mikey has a BMI of %fn", mikeyBMI); // Let the memory be recycled free(mikey); // Forget where it was mikey = NULL; return 0; }
Notice the operator ->. The code p->weightInKilos says, “Dereference the pointer p to the struct and get me the member called weightInKilos.”
This idea of structs on the heap is a very powerful one. It forms the basis for Objective-C objects, which we turn to next.