Node:Memory allocation, Next:union, Previous:struct, Up:Data structures
Most variables in C have a fixed size. For example, a string declared
to be 200 bytes long will always be 200 bytes long throughout the
program. Sometimes, however, you will need variables whose size can
vary. You might want a string whose size can vary between 0 and 100
kilobytes, for instance. We have already seen occasions where this sort
of string is needed with the getline
function. (See getline.)
This is where dynamic data, or data whose size can vary, comes in.
Dynamic data is created via the process of memory allocation, that
is, assigning a block of memory to a variable. Blocks of memory are
usually assigned with the malloc
function (the function name is
from the phrase "memory allocation"), and can be resized with the
realloc
("memory reallocation") function, and even merged back
into the pool of available memory with the free
function.
The malloc
function takes one argument, the number of bytes to
allocate. It returns a void pointer, which provides the address of the
beginning of a block of memory that the program can use. This void
pointer can be assigned to any other type of pointer. The only way to
make use of the block of memory that has been allocated is through its
pointer; in that sense, the block is not a "real" variable, that is to
say, you cannot assign a value to the memory block directly. Instead,
the address returned by malloc
enables you to use the block
indirectly; in this way, the block can contain any kind of value a real
variable can. Having to use blocks indirectly through pointers is a
small price to pay for the flexibility of dynamic data.
The following code example allocates a ten-byte string:
char *my_string; my_string = (char *) malloc(10+1);
Notice that the void pointer returned by malloc
is cast to a
character pointer (type char *
) before it is assigned to
my_string
. (See The cast operator.) Also notice that we have
actually allocated 11 bytes of space; this is because the 11th byte must
contain a null character that terminates the string but does not count
toward its actual length. Careful! The newly-allocated block
will be filled with garbage.
To reallocate the memory, use the realloc
function. This
function takes two parameters. The first is the pointer to the memory
block to be reallocated, and the second is a number of type
size_t
that specifies the new size for the block. It returns a
void pointer to the newly reallocated block. Here is how to reallocate
the block allocated for my_string
above, to a new size of 1000
bytes:
my_string = (char *) realloc (my_string, 1001);
The new block will contain all the data in the old block, followed by enough space to pad out the block to the new length. The new space will be filled with garbage.
Finally, to free up the memory allocated to a block and return it to the
common pool of memory available to your program, use the free
function, which takes only one argument, the pointer to the block you
wish to free. It does not return a value.
free (my_string);
It is also possible to allocate the memory for a structure when it is
needed and use the ->
operator to access the members of the
structure, since we must access the structure via a pointer. (See the
code sample following the next paragraph for an example of how to do
this.) If you are creating complex data structures that require
hundreds or thousands of structure variables (or more), the ability to
create and destroy them dynamically can mean quite a savings in memory.
It's easy enough to allocate a block of memory when you know you want
1000 bytes for a string, but how do you know how much memory to allocate
for a structure? For this task, C provides the sizeof
function,
which calculates the size of an object. For example, sizeof
(int)
returns the numbers of bytes occupied by an integer variable.
Similarly, sizeof (struct personal_data)
returns the number of
bytes occupied by our personal_data
structure. To allocate a
pointer to one of these structures, then set the year_of_birth
member to 1852, you would write something like the following:
struct personal_data* my_struct_ptr; my_struct_ptr = (struct personal_data*) malloc (sizeof (struct personal_data)); my_struct_ptr->year_of_birth = 1852;