Memory and Pointers






Memory

C is a fairly low level programming language. It allows you to control the allocation of memory in your program explicitly. Before we get into applications of this, though, we should first talk about how exactly memory is organized.

Everything stored in your computer is simply a series of bits, ones and zeros, where the one represents "on" and the zero represents "off." Bits are further grouped into bytes, which are each 2^3, or 8, bits long (most computer memory is dealt with in powers of two. That's why for instance a kilobyte is 1024 bytes, not 1000: 1024 is 2^10). Each byte has an associated address, which roughly corresponds to the location of that particular byte in the entire memory structure of the computer. Whenever you declare a variable, the C compiler goes along and allocates enough space to contain a value of that type. The bytes allocated are contiguous; that is, they have addresses that are consecutive. This is also true of larger variables like arrays, except that more memory will be allocated.

Pointers

Since every variable has a unique address in memory, C allows you refer to a variable by either its name or its address. The address-of operator in C is the ampersand (&). Variables that contain addresses are of a special type called pointers. The value of a pointer variable, then, is the address in memory it points to. You can also get the value of the spot in memory the pointer points to by deferencing the pointer via the dereference operator, the asterisk (*). So how do you declare a pointer variable? You must first declare the type of variable it points to; you can have pointer-to-int, pointer-to-char, or anything else. To tell the program that your variable is a pointer, you place an asterisk before its name, like so:

int i, *ptr;

Here, the variable i is a normal integer variable, while ptr is a pointer variable. Just like normal variables, pointers don't start out initialized. They may start out with the special value NULL, indicating that they don't point to anything, but it is not guaranteed. You can work with pointer in much the same way as you can with other variables, except that you usually should use either the address-of or dereference operators when dealing with both normal and pointer variables. Some examples are below:

i = 10; /* initialize i */

ptr = &i; /* now ptr points to i's address in memory */

printf("%d\n", *ptr); /* prints 10 */

i = ptr; /* BAD!  don't do this */

ptr = i; /* also bad */

Pointers are somewhat confusing at first, but they are very powerful once you get used to them.

Dynamic Arrays

Now, it turns out that the name of an array variable by itself without square brackets acts almost exactly like a pointer to the first ([0]) element of the array. But arrays can be annoying because you have to declare how big they're going to be right from the start. Well, arrays like that are called static arrays (no relation to the static keyword). C also has a feature called a dynamic array that is built with pointers. You start by declaring a normal pointer variable of whatever type you want the array to be. Then, you use the malloc() function from the stdlib.h standard library. malloc() takes on argument, a size in bytes, and returns a pointer to a block of memory that size, or NULL if it can't allocate enough memory (malloc() stands for "memory allocation"). The type of pointer it returns is a special type called a void pointer that doesn't have any specific type associated with it. For this reason, you should always cast the return value of a malloc() call. You do this the same way as with normal typecasting, but you add an asterisk to make the cast a pointer cast (sick of asterisks yet?). In this example, we want a function to make a dynamic array of integers that can contain a number of integers, with the number stored in a variable called num of type int:

int *MakeArray(int num)
{
   int *dynarray;
  
   dynarray = (int *) malloc(num * (sizeof(int)));
   if (dynarray == NULL) return NULL;
   else return dynarray;
}

We could, of course, have shortened this function by doing away with the variable declaration altogether, but for the purposes of this example, I kept it in. Notice how I used the sizeof operator to make sure the array has enough space for the desired number of integers. Without it, you would only have enough room for num characters, since characters are defined to take up one byte of space. The other variable types are machine-dependent, so the sizeof operator should be used. Once you have malloced a pointer, you can use it just as you would an array, with square brackets and everything.

Pointers also act like arrays in that any changes made to a pointer variable inside a function are saved, since you are operating on the actual address in memory where some information is saved. This is the easiest way to return multiple values from a function.


Back to Compound Data Types   On to Strings
Back to the Outline