Pointers are without a doubt one of the most important mechanisms in C. They are the means by which we implement call by reference function calls, they are closely related to arrays and strings in C, they are fundamental to utilising C's dynamic memory allocation features, and they can lead to faster and more efficient code when used correctly.
A pointer is a variable that is used to store a memory address. Most commonly the address is the location of another variable in memory.
If one variable holds the address of another then it is said to point to the second variable.
Address | Value | Variable |
---|---|---|
1000 | ||
1004 | 1012 | ivar_ptr |
1008 | ||
1012 | 23 | ivar |
1016 |
In the above illustration ivar is a variable of type int with a value 23 and stored at memory location 1012. ivar_ptr is a variable of type pointer to int which has a value of 1012 and is stored at memory location 1004. Thus ivar_ptr is said to point to the variable ivar and allows us to refer indirectly to it in memory.
NOTE : It should be remembered that ivar_ptr is a variable itself with a specific piece of memory associated with it, in this 32-bit case four bytes at address 1004 which is used to store an address.
Pointers like all other variables in C must be declared as such prior to use.
Syntax : type *ptr ;
which indicates that ptr is a pointer to a variable of type type.
For example
int *ptr ;
declares a pointer ptr to variables of type int.
NOTE : The type of the pointer variable ptr is int *. The declaration of a pointer variable normally sets aside just two or four bytes of storage for the pointer whatever it is defined to point to.
In 16-bit systems two byte pointers are termed near pointers and are used in small memory model programs where all addresses are just segment offset addresses and 16 bits in length. In larger memory model programs addresses include segment and offset addresses and are 32 bits long and thus pointers are 4 bytes in size and are termed far pointers.
In 32-bit systems we have a flat address system where every part of memory is accessible using 32-bit pointers.
& is a unary operator that returns the address of its operand which must be a variable.
For Example :-
int *m ; int count=125, i ;/* m is a pointer to int, count, i are integers */ m = &count ;
The address of the variable count is placed in the pointer variable m.
The * operator is the complement of the address operator & and is normally termed the indirection operator. Like the & operator it is a unary operator and it returns the value of the variable located at the address its operand stores.
For Example :-
i = *m ;
assigns the value which is located at the memory location whose address is stored in m, to the integer i. So essentially in this case we have assigned the value of the variable count to the variable i. The final situation is illustrated below.
One of the most frequent causes of error when dealing with pointers is using an uninitialised pointer. Pointers should be initialised when they are declared or in an assignment statement. Like any variable if you do not specifically assign a value to a pointer variable it may contain any value. This is extremely dangerous when dealing with pointers because the pointer may point to any arbitrary location in memory, possibly to an unused location but also possibly to a memory location that is used by the operating system. If your program tries to change the value at this address it may cause the whole system to crash. Therefore it is important to initialise all pointers before use either explicitly in your program or when defining the pointer.
A pointer may also be initialised to 0 ( zero ) or NULL which means it is pointing at nothing. This will cause a run-time error if the pointer is inadvertently used in this state. It is useful to be able to test if a pointer has a null value or not as a means of determining if it is pointing at something useful in a program.
NOTE : NULL is #defined in
For Example :-
int var1, var2 ; int *ptr1, *ptr2 = &var2 ; int *ptr3 = NULL ; ... ptr1 = &var1 ;
ptr1 and ptr2 are now pointing to data locations within the program so we are free to manipulate them at will i.e. we are free to manipulate the piece of memory they point to.
Recall when we wanted to swap two values using a function we were unable to actually swap the calling parameters as the call by value standard was employed. The solution to the problem is to use call by reference which is implemented in C by using pointers as is illustrated in the following example.
#include <stdio.h> void swap( int *, int * ) ; void main( ) { int a, b ; printf( "Enter two numbers" ) ; scanf( " %d %d ", &a, &b ) ; printf( "a = %d ; b = %d \n", a, b ) ; swap( &a, &b ) ; printf( "a = %d ; b = %d \n", a, b ) ; } void swap ( int *ptr1, int *ptr2 ) { int temp ; temp = *ptr2 ; *ptr2 = *ptr1 ; *ptr1 = temp ; }
The swap() function is now written to take integer pointers as parameters and so is called in main() as
swap( &a, &b );
where the addresses of the variables are passed and copied into the pointer variables in the parameter list of swap(). These pointers must be de-referenced to manipulate the values, and it is values in the the same memory locations as in main() we are swapping unlike the previous version of swap where we were only swapping local data values.
In our earlier call-by-value version of the program we called the function from main() as swap(a,b); and the values of these two calling arguments were copied into the formal arguments of function swap.
In our call-by-reference version above our formal arguments are pointers to int and it is the addresses contained in these pointers, (i.e. the pointer values), that are copied here into the formal arguments of the function. However when we de-reference these pointers we are accessing the values in the main() function as their addresses do not change.
All Rights Reserved. © 2024 BookOfNetwork