5.2. C Pointers Primer

5.2.1. Pointer Basics

Pointers can be a difficult aspect of C programming and their misuse leads to more problems than any other part of the language. With a proper understanding of the principles, however, things are much easier. To become good at EMBOSS programming you must master at least the basics of pointers.

With the exception of register variables, every variable you declare in your program resides somewhere in memory, that "somewhere" is the memory address of the variable. A pointer is merely a variable whose value is such a memory address. On most C implementations the longest unsigned integer that the hardware supports is used for pointers, for example an unsigned long int. The value of this integer is the memory address of another object, such as another variable, C data structure etc.

So, when this line of a program is executed:

ajint x=0;

sufficient memory to hold an integer (usually 4 bytes) is reserved for use by the program. The value of those bytes is set to zero.

To declare a pointer variable, you use the pointer operator (*) in the declaration. So when this line is called:

ajint *ptr=NULL;

memory for a pointer variable is reserved and the value of the variable set to NULL.

To return the memory address of a variable, you use the & (address) operator:

ajint x=10;
ajint *ptr=NULL;

ptr = &x;

To get to the value held at a particular memory address you use the * (pointer) operator; this is called dereferencing the pointer or getting a value by indirection:

ajint x=10;
ajint y=0;
ajint *ptr=NULL;

ptr = &x;
y = *ptr;  /* y is set to 10 */ 

From the above code it's clear that x and y hold integer values or, put more simply, "x and y are integers". Similarly, ptr holds a memory address or, more simply, "ptr is a pointer". The pointer is made to point to x in the above code.

If you don't complicate the basic idea that a pointer is a variable whose value is a memory address then you have the foundation for understanding and using pointers in EMBOSS.

Example. Consider the following code:

int main()
              ajint x=0;

   /* 1. */   printf("Value of x : %d\n", x);                        
   /* 2. */   printf("Memory address of x : %p\n", &x);
   /* 3. */   printf("Value of x by indirection : %d\n", *(&x));

/* Output will look like:

Value of x : 0
Memory address of x : #1 
Value of x by indirection : 0

(In reality, a hexadecimal number would be printed instead of '#1', but '#1' is easier to follow).

The variable name x is our handle on the reserved memory. It refers to an integer value that resides at memory address #1. In the code the following is done:

  1. First print the value of x.

  2. Then use & to get the memory address of variable x and print it.

  3. Then use the * operator to dereference this address and print the value stored there. Declaring Pointers

In practice a pointer holds the memory address of a specific object such as an integer, C data structure or even another pointer. The type of data pointed at must be specified when the pointer is declared. This is not because the memory address of an int is any different to that of a float, it's so that the compiler knows how the pointer can be used in the source code. For instance the compiler must know the type of data pointed at to be able to access the memory pointed at in order to, for example, print a value correctly. This is why in C pointers are declared in the manner shown:

   ajint *ptr=NULL;

The * in the declaration means that ptr is a memory address and the ajint tells us that it's the address of an integer, i.e. ptr is a pointer to an integer.

When that line of the program is executed, sufficient memory to hold a memory address will be reserved for use by our program. This, like an integer, is normally 4 bytes on 32 bit machines. The value of these 4 bytes is set to NULL.

It's important to appreciate that the meaning of the * operator is context-dependent. It's only in the context of a variable declaration that it declares a variable as being a pointer, for example:

ajint *ptr=NULL

means "ptr is a pointer variable, set its value to NULL".

If * was found outside of a variable declaration it would indicate a variable being dereferenced as mentioned above, for example:

ajint *ptr = NULL;

*ptr = 0;

Here the second instance of *ptr = 0; means "dereference ptr and set the value stored there to 0" or in other words "set the value held at memory address ptr to 0". Note that the code, as written, would crash - but it is only illustrative.

The final thing to mention is that we've assigned the value of NULL to the pointer in the declaration and the integer stored at that memory address to 0 in the code. They cannot be used interchangeably as they are not of the same type: you should only ever use NULL for pointers.

You can see that in the code below:

int main()
/* 1. */  ajint x=0;
/* 1. */  ajint *ptr=NULL;

/* 2. */  printf("Value of x : %d\n", x);
/* 3. */  ptr = &x;
/* 4. */ *ptr=5;
/* 2. */  printf("Value of x : %d\n", x);

/* Output will look like this:
Value of x : 0
Value of x : 5

In the code:

  1. Declare an integer and a pointer to an integer.

  2. Print the value of x.

  3. Give ptr the value of the address of x.

  4. Set the value of x to 5 by indirection.

In the above example, you would normally say that "ptr holds the address of x" or simply "ptr points to x".

5.2.2. Pointers to Pointers

It was mentioned above that a pointer can hold the memory address of another pointer. This is obvious when you realise that a pointer, like any variable, resides somewhere in memory. So if a pointer that holds the memory address of an integer is a 'pointer to an integer', then a pointer that holds the memory address of another pointer is, of course, 'a pointer to a pointer'.

This bit of code shows how you declare a pointer to a pointer-to-an-integer:

    ajint **ptrto=NULL;

For better comprehension let us write the declaration as follows:

    (ajint *)  *ptrto=NULL;

The second * means that ptrto is a memory address. The ajint * tells us that it's the address of a pointer-to-an-integer. When the code is executed, enough memory to hold an address is reserved for our use and the value of the bytes is set to NULL.

Of course, the & (address) and the * (pointer) operators still work with pointers to pointers. Where you have multiple levels of pointers you can use multiple * (pointer) operators for dereferencing. *ptrto would dereference once and retrieve an address (a pointer to an integer). **ptrto would dereference twice and retrieve an integer.

You can see that in the code below:

/* 1. */ ajint x=0;          /* an integer */
/* 1. */ ajint *ptr=NULL;    /* a pointer to an integer */
/* 1. */ ajint **ptrto=NULL; /* a pointer to a pointer-to-an-integer */

/* 2. */ printf("Address of x : %p\n", &x); 
/* 2. */ printf("Address of ptr : %p\n", &ptr); 
/* 2. */ printf("Address of ptrto : %p\n", &ptrto); 

/* 3. */ ptr = &x; 
/* 3. */ ptrto = &ptr; 

/* 4. */ printf("Value of x : %d\n", x); 
/* 4. */ printf("Value of ptr : %p\n", ptr); 
/* 4. */ printf("Value of ptrto : %p\n", ptrto); 

/* 5. */ printf("Value of x by dereferencing ptr : %d\n", *ptr); 
/* 5. */ printf("Value of x by dereferencing ptrto : %d\n", **ptrto); 

/* Output will look like this:
Address of x : #1 
Address of ptr : #2 
Address of ptrto : #3 

Value of x : 0 
Value of ptr : #1     /* i.e. the address of x*/
Value of ptrto : #2   /* i.e. the address of ptr*/

Value of x by dereferencing ptr : 0 
Value of x by dereferencing ptrto : 0 

There are no new concepts in the above code, it's merely an extension of what you already know about pointers:

  1. We start by declaring 3 variables called x, ptr and ptrto. x is the integer, ptr is a pointer-to-an-integer and ptrto is a pointer to a pointer-to-an-integer.

  2. We then print the address of each variable; x lives at #1, ptr at #2 and ptrto at #3.

  3. We assign the address of x to ptr. The address of ptr is assigned to ptrto.

  4. Print the value of each variable. x has a value of 0, ptr has a value of the address of x, i.e. #1 and ptrto has the value of the address of ptr, i.e. #2.

  5. Print x out by indirection.

You already know what *ptr means. Further on ptrto is dereferenced twice, which is what you've got to do if you want to get to the integer from it. The first time you dereference ptrto you get to ptr, the second time you are effectively dereferencing ptr, which takes you to x

This, and in fact all operations with pointers, is very easily understood if you sketch what's happening on a piece of paper: draw a diagram if you're not sure what's happening with your pointers.