This Module may be one of the toughest topics in C / C++ that complained by students :o), although it is one of the most important topic. The source code for this module is available at C/C++ pointers program source codes. The lab worksheets for your practice are: C/C++ pointers part 1 and C/C++ pointers part 2. Also the the related exercises in the Indirection Operator lab worksheet 1, lab worksheet 2 and lab worksheet 3.
8.1 What Is Pointers?
int rate = 100;
Then the variable is stored at a specific memory address and as an illustration, can be depicted as follows:
You can see, the memory address of the variable rate (or any other variable) is a number, so can be treated like any other number in C/C++. Normally the number is in hexadecimal format because computer system numbering system is based on binary and this binary is simplified by using hexadecimal representation.
Then, if a variable’s memory address is known, the programmer can create a second variable for storing a memory address of the first variable.
From the above Figure, we can declare another variable to hold the memory address of variable rate; let say gives it a name, s_rate (Figure 8.2).
At the beginning, s_rate is uninitialized. So, storage has been allocated for s_rate, but its value is undetermined, as shown below.
Let store the memory address of variable rate, in variable s_rate, so, s_rate now contains the memory address of rate, which indicates its storage location in memory where the actual data (100) is stored.
Finally, in C/C++ vocabulary, s_rate is pointing to rate or is said a pointer to rate and final illustration is shown below.
In simplified form:
So the declaration of the pointer variable becomes something like this:
In other word, the variable s_rate contains the memory address of the variable rate and is therefore a pointer to rate. The asterisk (*) is used to show that is it the pointer variable instead of normal variable.
The definition: A pointer is a variable that contains the memory address of another variable, where, the actual data is stored.
By using pointers, it is an efficient way of accessing and manipulating data. From user side implicitly, it contains the actual location of your data, where the compiler can find that data in memory.
This is very useful for dynamic and efficient memory management programming as we know memory, registers and cache are scarce in computer system.
Why it is very efficient way? One of the reasons is because for every computer system, it is provided with fixed and limited amount of memory for temporary data storage and processing.
During the computers operation, for example, the initialization of the Operating System and programs (processes) running, there are loading and unloading data into and from, moving data from and to different locations of the memory, including the cache memory and the processors’ registers for temporary data storage and processing. This loading and unloading need an efficient memory management.
Furthermore some of the memory portion of computer system also used for shared data to further optimizes the utilization. This shared data access (read/write) depends heavily on the pointers manipulation.
Without moving data from here to there in computer system, we just can use pointers to point to different locations.
Apart from primary memory, secondary memory also involved in this memory management through swapping and paging.
Newer computer applications such as real time processing, graphic manipulations and complex mathematical calculations widely known for their memory intensive applications need storage efficiency to fully utilize the scarce computer memory resources.
By using pointers, the use of this fixed, limited and shared resources should be optimized. The unloaded resources from memory will release it for other applications utilization.
Yes, you can add extra memory module but without the utilization ‘optimization’, it is not fully utilized somewhere, not sometime but most of the times :o).
Familiar with the dead blue screen? Pointers also generate many bugs in programs if used improperly or maliciously :o).
A pointer is a numeric variable and like other variables, as you have learned before, must be declared and initialized before it can be used.
The following is a general form for declaring a pointer variable:
int * type_of_car;
data_type is any valid C/C++ type. It is any valid C/C++ pointer base type such as char, int, float or other valid C/C++ derived types. It indicates the type of the variable’s data to which the pointer points.
The pointer_variable_name follows the same rules as other variables naming convention and must be unique.
From the above pointer declaration example, in the first line, the declaration tells us that x is a pointer to a variable of type char.
The asterisk (*) is called indirection operator, and it indicates that x is a pointer to type char and not a normal variable of type char. Note the position of the *, it is valid for all the three positions.
Pointers can be declared along with non pointer variables as shown below:
// ch1 and ch2 both are pointers to type char.
char *ch1, *ch2;
// value is a pointer to type float, and percent is an ordinary float variable.
float *value, percent;
Once a pointer is declared, the programmer must initialize the pointer, that is, make the pointer point to something. Don’t make it point to nothing; it is dangerous.
Like regular variables, uninitialized pointers will not cause a compiler error, but using an uninitialized pointer could result in unpredictable and potentially disastrous outcomes.
Until pointer holds an address of a variable, it isn’t useful.
C/C++ uses two pointer operators:
Indirection operator (*) – has been explained previously.
Address-of-operator (&) – means return the address of.
When the & operator is placed before the name of a variable, the address-of-operator returns the memory address of the variable/operand.
int location = 200;
m = &location;
printf("The data, *m = %d\n",*m);
printf("The address where the data pointed to, m = %d\n", m);
// declare a pointer variable, m of type int
// assign the address of variable location
// to variable m, so pointer m is pointing to variable location
m = &location;
// the actual data assigned to variable location
location = 200;
Let re-examine the indirection operator (*). Actually * is a complement of &. It means returns the value of the variable located at the address that follows.
From the previous example:
m = &location;
location = 200;
q = *m;
The q = *m; statement will place the actual data value stored in variable location into variable q. That means q receives the actual data value stored at the memory address hold by variable m or the value stored in the variable location pointed to by variable m. Very confused huh?
The * operator appears before a pointer variable in only two places:
When declaring a pointer variable.
When dereferencing a pointer variable (to find the data it points to).
Only the addition and subtraction operations are permitted in expression involving pointers.
As with any variable, a pointer may be used on the right hand side of an assignment statement to assign its value to another pointer as shown in the following example.
// program to illustrate the basic use of pointers
using namespace std;
// declares an integer variable and two pointers variables
int num = 10, *point_one, *point_two;
// assigns the address of variable num to pointer point_one
point_one = #
// assigns the (address) point_one to point_two
point_two = point_one;
cout<<"*point_one = "<<*point_one<<"\n";
cout<<"*point_two = "<<*point_two<<"\n";
cout<<"num = "<<num<<"\n";
// displays value 10 stored in num since point_one
// and point_two now point to variable num
cout<<"\n-Both pointer point_one and"<<"\n";
cout<<"-point_two point to the same variable num."<<"\n";
cout<<"-That is why, they have same value, 10."<<endl;
The program example can be illustrated graphically as shown below. The memory address is arbitrarily chosen.
Notice the difference between memory address and the actual data value, which stored at the memory address. Do not worry about the addresses, they are determined and provided by the system.
From the above example, we can:
Access the contents of a variable by using the variable name (num) and is called direct access.
Access the contents of a variable by using a pointer to the variable (*point_one or *point_two) and is called indirect access or indirection.
As a conclusion, if a pointer named pter of type int has been initialized to point to the variable named var, the following are true:
// declare a pointer variable named pter, where the
// data stored pointed to by pter is int type
// assign the address of variable named var to a pointer variable named pter
pter = &var;
*pter and var both refer to the contents of var (that is, whatever data value stored there).
pter and &var refer to the address of var (the pter only hold the address of variable var not the actual data value).
So, a pointer name without the indirection operator (*) accesses the pointer value itself, which is of course, the address of the variable pointed to. Very confused :o).
// a basic pointer use
// declare and initialize an int variable
int var = 34;
// declare a pointer to int variable
// initialize ptr to point to variable var
ptr = &var;
// access var directly and indirectly
printf("\nDirect access, variable var value = var = %d", var);
// you can use %p for the pointer memory address directly or
// %0x or %0X in hexadecimal representative instead of
// %d, just to avoid confusion here…
printf("\nIndirect access, variable var value = *ptr = %d", *ptr);
// display the address of var two ways
printf("\n\nThe memory address of variable var = &var = %d", &var);
printf("\nThe memory address of variable var = ptr = %d\n", ptr);
The address displayed for variable var may not be 4094 on your system because different computer will have different specification.
For pointer arithmetic operation, only two arithmetic operations, that is addition and subtraction available. For example:
int age = 25;
So, C / C++ reserved storage for the variable age and store the value 25 in it.
Let say, C / C++ has stored this value at the memory address 1000, and we declare a pointer variable named ptr_age that point to the variable age.
Then after the following expressions have been executed:
ptr_age = &age;
The content of the pointer then becomes 1002, not 1001 anymore (integer value takes two byte so C/C++ add 2 to the pointer).
Different variable types occupy different amount of memory also depend on the platform used, whether 16, 32 or 64 bits system. For example for 16 bits platform:
int = 2 byte.
float = 4 byte.
Each time the pointer is incremented, it points to the next integer and similarly, when a pointer is decremented, it points to the previous integer.
Each individual byte of memory has it own address; so multi-byte variable actually occupies several addresses.
When pointers used to handle the addresses of multi-byte variables, the address of a variable is actually the address of the lowest byte it occupies.
int vint = 12252;
char vchar = 90;
float vfloat = 1200.156004;
These variables are stored in memory as shown below. The pointer is pointing to the lowest byte.
int variables occupy 2 byte starts at 1000.
char variables occupy 1 byte starts at 1003.
float variables occupy 4 byte starts at 1006.
Then, how to declare and initialized pointers to these 3 variables? As explained to you before, we start with pointers variable declaration similar to other variable declaration, then initialization that giving the pointer an initial value and finally use the pointers.
Declaration - declare the pointer variables:
Initialization - assign the addresses of the normal variables to the new pointer variables.
p_vint = &vint;
p_vchar = &vchar;
p_vfloat = &vfloat;
Then pointer is equal to the address of the first byte of the pointed-to variable. So:
p_vint equals 1000.
p_vchar equals 1003.
p_vfloat equals 1006.
We have to understand this concept because we are dealing with addresses, not the actual data as before.
Other pointer arithmetic operation is called differencing, which refers to subtracting 2 pointers.
For example, two pointers that point to different elements of the same array can be subtracted to find out how far apart they are.
Pointer arithmetic automatically scales the answer, so that it refers to the array elements.
Thus, if ptr1 and ptr2 point to elements of an array (of any type), the following expression tells you how far apart the elements are:
ptr1 – ptr2;
The comparison is valid only between pointers that point to the same array.
Under this circumstances, the following relational operators work for pointers operation.
==, !=, >, <, >=, and <=
A lower array element that is those having a smaller subscript, always have a lower address than the higher array elements.
Thus if ptr1 and ptr2 point to elements of the same array, the following comparison:
ptr1 < ptr2 is TRUE
If ptr1 points to an earlier member of the array than ptr2 does.
Many arithmetic operations that can be performed with regular variables, such as multiplication and division, do not work with pointers and will generate errors in C/C++.
The following table is a summary of pointer operations.
1. Assignment (=)
You can assign a value to a pointer. The value should be an address with the address-of-operator (&) or from a pointer constant (array name)
2. Indirection (*)
The indirection operator (*) gives the value stored in the pointed to location.
3. Address of (&)
You can use the address-of operator to find the address of a pointer, so you can use pointers to pointers.
You can add an integer to a pointer to point to a different memory location.
You can subtract an integer from a pointer to point to a different memory location.
Valid only with 2 pointers that point to the same array.
Table 8.1: Pointer operations
Let say we declare a pointer something like this:
This statement declares a pointer to type int but not yet initialized, so it doesn’t point to anything known value (address).
Then, consider the following pointer assignment statement:
// this is not an address lol!
*ptr = 12;
This statement means the value 12 is assigned to whatever address ptr point to. That address can be almost anywhere in memory that may contain any data.
The 12 stored in that location may overwrite some important information, and the result can be anything from storage program errors to a full system crash. So, you must initialize a pointer so that it point to something. Do not create a stray pointer.
A better solution may be you can assign NULL (\0) value during the initialization before using the pointer, by pointing it to something useful or for pointer that point to a string you may just point to an empty string for dummy such as:
char * mystring = "";
Take note that in the .NET C/C++ programming the nullptr is used instead of NULL. The NULL/nullptr is a pointer that point to the 0x0000 (32 bits system) memory address.
A program example:
int *thepointer = NULL;
// do some testing....
printf("The thepointer pointer is pointing to = %X\n", thepointer);
printf("The thepointer pointer is pointing to = %d\n", thepointer);