Uses.
Pointers are directly supported without restrictions in languages such as PL/I, C, C++, Pascal, and most assembly languages. They are primarily used for constructing references, which in turn are fundamental to constructing nearly all data structures, as well as in passing data between different parts of a program.
In functional programming languages that rely heavily on lists, pointers and references are managed abstractly by the language using internal constructs like cons.
When dealing with arrays, the critical lookup operation typically involves a stage called address calculation which involves constructing a pointer to the desired data element in the array. If the data elements in the array have lengths that are divisible by powers of two, this arithmetic is usually a bit more efficient. Padding is frequently used as a mechanism for ensuring this is the case, despite the increased memory requirement. In other data structures, such as linked lists, pointers are used as references to explicitly tie one piece of the structure to another.
Pointers are used to pass parameters by reference. This is useful if the programmer wants a function's modifications to a parameter to be visible to the function's caller. This is also useful for returning multiple values from a function.
Pointers can also be used to allocate and de-allocate dynamic variables and arrays in memory. Since a variable will often become redundant after it has served its purpose, it is a waste of memory to keep it, and therefore it is good practice to de-allocate it (using the original pointer reference) when it is no longer needed. Failure to do so may result in a memory leak (where available free memory gradually, or in severe cases rapidly, diminishes because of an accumulation of numerous redundant memory blocks).
C pointers.
The basic syntax to define a pointer is:
int *ptr;
This declares
ptr
as the identifier of an object of the following type:- pointer that points to an object of type
int
This is usually stated more succinctly as '
ptr
is a pointer to int
.'
Because the C language does not specify an implicit initialization for objects of automatic storage duration,[5] care should often be taken to ensure that the address to which
ptr
points is valid; this is why it is sometimes suggested that a pointer be explicitly initialized to the null pointer value, which is traditionally specified in C with the standardized macroNULL
:int *ptr = NULL;
Dereferencing a null pointer in C produces undefined behavior, which could be catastrophic. However, most implementations simply halt execution of the program in question, usually with a segmentation fault.However, initializing pointers unnecessarily could hinder program analysis, thereby hiding bugs. In any case, once a pointer has been declared, the next logical step is for it to point at something: int a = 5;
int *ptr = NULL; ptr = &a;
This assigns the value of the address of
a
to ptr
. For example, if a
is stored at memory location of 0x8130 then the value of ptr
will be 0x8130 after the assignment. To dereference the pointer, an asterisk is used again: *ptr = 8;
This means take the contents of
ptr
(which is 0x8130), "locate" that address in memory and set its value to 8. If a
is later accessed again, its new value will be 8.
This example may be clearer if memory is examined directly. Assume that
a
is located at address 0x8130 in memory and ptr
at 0x8134; also assume this is a 32-bit machine such that an int is 32-bits wide. The following is what would be in memory after the following code snippet is executed: int a = 5; int *ptr = NULL;
Address Contents 0x8130 0x00000005 0x8134 0x00000000
(The NULL pointer shown here is 0x00000000.) By assigning the address of
a
to ptr
:ptr = &a;
yields the following memory values:
Address Contents 0x8130 0x00000005 0x8134 0x00008130
Then by dereferencing
ptr
by coding:*ptr = 8;
the computer will take the contents of
ptr
(which is 0x8130), 'locate' that address, and assign 8 to that location yielding the following memory:Address Contents 0x8130 0x00000008 0x8134 0x00008130
Clearly, accessing
a
will yield the value of 8 because the previous instruction modified the contents of a
by way of the pointer ptr
.C Arrays.
In C, array indexing is formally defined in terms of pointer arithmetic; that is, the language specification requires that
array[i]
be equivalent to *(array + i)
. Thus in C, arrays can be thought of as pointers to consecutive areas of memory (with no gaps), and the syntax for accessing arrays is identical for that which can be used to dereference pointers. For example, an array array
can be declared and used in the following manner:int array[5]; /* Declares 5 contiguous integers */ int *ptr = array; /* Arrays can be used as pointers */ ptr[0] = 1; /* Pointers can be indexed with array syntax */ *(array + 1) = 2; /* Arrays can be dereferenced with pointer syntax */ *(1 + array) = 3; /* Pointer addition is commutative */ 2[array] = 4; /* Subscript operator is commutative */
This allocates a block of five integers and names the block
array
, which acts as a pointer to the block. Another common use of pointers is to point to dynamically allocated memory from mal loc which returns a consecutive block of memory of no less than the requested size that can be used as an array.
While most operators on arrays and pointers are equivalent, it is important to note that the
sizeof
operator will differ. In this example, sizeof(array)
will evaluate to5*sizeof(int)
(the size of the array), while sizeof(ptr)
will evaluate to sizeof(int*)
, the size of the pointer itself.
Default values of an array can be declared like:
int array[5] = {2,4,3,1,5};
If you assume that
array
is located in memory starting at address 0x1000 on a 32-bit little-endian machine then memory will contain the following (values are in hexadecimal, like the addresses):0 1 2 3 1000 2 0 0 0 1004 4 0 0 0 1008 3 0 0 0 100C 1 0 0 0 1010 5 0 0 0
Represented here are five integers: 2, 4, 3, 1, and 5. These five integers occupy 32 bits (4 bytes) each with the least-significant byte stored first (this is a little-endian CPU architecture) and are stored consecutively starting at address 0x1000.
The syntax for C with pointers is:
array
means 0x1000array+1
means 0x1004 (note that the "+1" really means to add one times the size of anint
(4 bytes) not literally "plus one")*array
means to dereference the contents ofarray
. Considering the contents as a memory address (0x1000), look up the value at that location (0x0002).array[i]
means element numberi
, 0-based, ofarray
which is translated into*(array + i)
The last example is how to access the contents of
array
. Breaking it down: array + i
is the memory location of the (i+1)th element ofarray
*(array + i)
takes that memory address and dereferences it to access the value.
E.g.
array[3]
is synonymous with *(array+3)
, meaning *(0x1000 + 3*sizeof(int))
, which says "dereference the value stored at 0x100C
", in this case 0x0001
. C Linked List.
Below is an example definition of a linked list in C.
/* the empty linked list is represented by NULL * or some other sentinel value */ #define EMPTY_LIST NULL struct link { void *data; /* data of this link */ struct link *next; /* next link; EMPTY_LIST if there is none */ };
Note that this pointer-recursive definition is essentially the same as the reference-recursive definition from the Haskell programming language:
data Link a = Nil | Cons a (Link a)
Nil
is the empty list, and Cons a (Link a)
is a cons cell of type a
with another link also of type a
.
The definition with references, however, is type-checked and does not use potentially confusing signal values. For this reason, data structures in C are usually dealt with via wrapper functions, which are carefully checked for correctness.
Pass-By-Address Using Pointers.
Pointers can be used to pass variables by their address, allowing their value to be changed. For example consider the following C code:
/* a copy of the int n can be changed within the function without affecting the calling code */ void passByValue(int n) { n = 12; } /* a pointer to m is passed instead. No copy of m itself is created */ void passByAddress(int *m) { *m = 14; } int main(void) { int x = 3; /* pass a copy of x's value as the argument */ passByValue(x); // the value was changed inside the function, but x is still 3 here afterwards /* pass x's address as the argument */ passByAddress(&x); // x was actually changed by the function and is now equal to 14 here return 0; }
Dynamic Memory Allocation.
Pointers are used to store and manage the addresses of dynamically allocated blocks of memory. Such blocks are used to store data objects or arrays of objects. Most structured and object-oriented languages provide an area of memory, called the heap or free store, from which objects are dynamically allocated.
The example C code below illustrates how structure objects are dynamically allocated and referenced. The standard C library provides the function
malloc()
for allocating memory blocks from the heap. It takes the size of an object to allocate as a parameter and returns a pointer to a newly allocated block of memory suitable for storing the object, or it returns a null pointer if the allocation failed. /* Parts inventory item */ struct Item { int id; /* Part number */ char * name; /* Part name */ float cost; /* Cost */ }; /* Allocate and initialize a new Item object */ struct Item * make_item(const char *name) { struct Item * item; /* Allocate a block of memory for a new Item object */ item = (struct Item *)malloc(sizeof(struct Item)); if (item == NULL) return NULL; /* Initialize the members of the new Item */ memset(item, 0, sizeof(struct Item)); item->id = -1; item->name = NULL; item->cost = 0.0; /* Save a copy of the name in the new Item */ item->name = (char *)malloc(strlen(name) + 1); if (item->name == NULL) { free(item); return NULL; } strcpy(item->name, name); /* Return the newly created Item object */ return item; }
The code below illustrates how memory objects are dynamically deallocated, i.e., returned to the heap or free store. The standard C library provides the function
free()
for deallocating a previously allocated memory block and returning it back to the heap./* Deallocate an Item object */ void destroy_item(struct Item *item) { /* Check for a null object pointer */ if (item == NULL) return; /* Deallocate the name string saved within the Item */ if (item->name != NULL) { free(item->name); item->name = NULL; } /* Deallocate the Item object itself */ free(item); }
Memory-Mapped Hardware.
On some computing architectures, pointers can be used to directly manipulate memory or memory-mapped devices.
Assigning addresses to pointers is an invaluable tool when programming micro controllers. Below is a simple example declaring a pointer of type int and initialing it to a hexadecimal address in this example the constant
0x7FFF
:int *hardware_address = (int *)0x7FFF;
In the mid 80s, using the BIOS to access the video capabilities of PCs was slow. Applications that were display-intensive typically used to access CGA video memory directly by casting the hexadecimal constant
0xB8000
to a pointer to an array of 80 unsigned 16-bit int values. Each value consisted of an ASCII code in the low byte, and a color in the high byte. Thus, to put the letter 'A' at row 5, column 2 in bright white on blue, one would write code like the following:#define VID ((unsigned short (*)[80])0xB8000) void foo() { VID[4][1] = 0x1F00 | 'A'; }