http://cpp.gantep.edu.tr
C++ resources. Faculty of Engineering, University of Gaziantep
 MAIN  -  Tutorials  -  Howtos  -  nmPrimer  -  Misc  -  Links  -  Contacts
C++ Tutorial (Advanced)
[index][.][1][2][3][4][5][6][>]

1. References and Pointers

Last modified: Tue, 02 Nov 2010 22:34:04 +0200

Complete: ###################- (95%)

1.1 Introduction
1.2 Variables and Memory Addresses
1.3 References
1.4 Pointers
1.5 Pointer Arithmetic
1.6 Pointers and Arrays
1.7 Use of Pointers in Functions
1.8 The NULL Pointer
1.9 Examples
1.10 Exercises

1.1 Introduction

The most powerful tools of the C++ programming language are pointers and references that allows the programmer to manipulate computer memory directly. In this section, at the introductory level, we will learn what references and pointers are and how to declare and use them.

1.2 Variables and Memory Addresses

Computer memory can be considered as a very large array of bytes. For example, a computer with 1 GB of RAM (Random Access Memory) actually contains an array of 1024 x 1024 x 1024 = 1,073,741,824 bytes. As an array, these bytes are indexed from 0 to 1,073,741,824. The index of each byte is known as its memory address. So a 1 GB computer has memory addresses ranging from 0 to 1,073,741,823, which is 0x00000000 to 0x3fffffff in hexadecimal. Fig 10.1 represents that array of bytes, each with its hexadecimal address.


Fig 10.1: Array of bytes and addresses in hexadecimal

When a variable is declared and assigned a value, four fundamental attributes are associated with it:

  • its name
  • its type
  • its value (content)
  • its address
For example, the declaration
  int n = 25;
associates the name n, the type int, and the address of some location in memory where the value of n is stored. Suppose that address is 0x0024fdf0. Then we can visualize the variable n like this.

On most computers, a variable of type int is 4-byte long in memory. So the variable n shown above would occupy the 4-byte block of memory represented by the shaded rectangles in the Fig 10.2. Note that, the address of this object, which is called the region of storage, is the address of the first byte in the block of memory, i.e. 0x0024fdf0.


Fig 10.2: Representation of an integer variable in the memory

In C++, the value of a variable is accessed via its name. The address of a variable is accessed via the address operator &. The expression &n evaluates to the address of the variable n. The use of this operator is illustrated in the following program.

01prg01.cpp: The use of address operator
  1:
  2:
  3:
  4:
  5:
  6:
  7:
  8:
  9:
 10:
 11:
 12:
// The use of the address operator
#include <iostream>
using namespace std;

int main(){
  int n = 25;

  cout << "n = " << n << endl;
  cout << "&n = " << &n << endl;

  return 0;
}
n = 25
&n = 0x0024fdf0

1.3 References

A reference is an alias. When you generate a reference, you initialize it with the name of another object, the target. Hence, the reference acts as an alternative name for the target. That is, anything you do to the reference is really done to the target. A reference is declared by using a reference operator &. A basic use of the reference is shown in the following program.

01prg02.cpp: A basic use of the reference
  1:
  2:
  3:
  4:
  5:
  6:
  7:
  8:
  9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
// A basic use of the reference
#include <iostream>
using namespace std;

int main(){
  int n = 25;  // the target
  int &r = n;  // the alias of the target

  cout << "1.  n and r = " << n << '\t' << r << endl;

  n = 40;
  cout << "2.  n and r = " << n << '\t' << r << endl;

  r = 12;
  cout << "3.  n and r = " << n << '\t' << r << endl;

  return 0;
}
1.  n and r = 25     25
2.  n and r = 40     40
3.  n and r = 12     12

 Note that
  • The reference operator (&) is the same symbol as used for the address operator. These are not the same operators, however, they are related.
  • Like constants, when a reference is declared it must initialized.

A reference is not a variable. A variable is an object; i.e. the region of storage. Different objects must occupy disjoint blocks of memory. However, the address of a reference and corresponding variable is the same. This is illustrated the program given below.

01prg03.cpp: The memory address of a variable and its alias
  1:
  2:
  3:
  4:
  5:
  6:
  7:
  8:
  9:
 10:
 11:
 12:
 13:
 14:
 15:
// The memory address of a variable and its alias
#include <iostream>
using namespace std;

int main(){
  int n = 25;  // the target
  int& r = n;  // the alias of the target

  cout << "n and r   = " << n << '\t' << r << endl;

  n = 40;
  cout << "&n and &r = " << &n << '\t' << &r << endl;

  return 0;
}
n and r = 25    25
&n and &r = 0x0024fdf0    0x0024fdf0

More than one reference can be associated with the same variable. For example, in the following code, r1, r2 and r3 all represent val1.
  int val;
  int &r1 = val;  // r1 is an alias for val
  int &r2 = val;  // r2 is another alias for val
  int &r3 = r1;   // r3 is another alias for val

References are used mostly for reference parameters in a function (see Section 6.1).

1.4 Pointers

Every variable has an address. We can also store the address of a variable in another variable, called pointer. Basically, a pointer is a variable that holds a memory address.

The pointer is a numerical variable. So, like other variables, before using a pointer it has to be declared by using indirection operator (*). The general form of the declaration is

  data-type *pointer-name;
For example
  char  *ch;     // a single character pointer
  int   *mass;   // an integer pointer
  float *xPos;   // a single-precision real pointer
One can use the address operator (&) to assign the memory address of a variable to a pointer. Note that, since a pointer is a variable itself, it is stored in another memory location. A simple usage of the pointer is demonstrated in the following program.

01prg04.cpp: Use of a pointer variable
  1:
  2:
  3:
  4:
  5:
  6:
  7:
  8:
  9:
 10:
 11:
 12:
 13:
 14:
// Use of a pointer variable
#include <iostream>
using namespace std;

int main(){
  int   n = 25;
  cout << "n   = " << n << '\n' << "&n  = " << &n << endl;

  int *pn = &n;  // pn holds the address of n

  cout << "pn  = " << pn << '\n' << "&pn = " << &pn << endl;

  return 0;
}
n   = 25
&n  = 0x0022ff74
pn  = 0x0022ff74
&pn = 0x0022ff70

Here, in line 9, the variable pn is called a 'pointer', because its value 'points' to the location of another value. Briefly, we say that pn points to n. Note that, the 'value' of pn is an address and it is stored in another memory location (since pn is a variable itself). The assignments in this program can be visualized as follows:

The indirection operator (*) is also called as dereference operator. If pn points to n, we can obtain the value of n directly from pn using the dereference operator *. The expression *pn evaluates to the value of n. This evaluation is called dereferencing the pointer pn.

01prg05.cpp: Use of dereferencing
  1:
  2:
  3:
  4:
  5:
  6:
  7:
  8:
  9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
// Use of dereferencing
#include <iostream>
using namespace std;

int main(){
  int   n = 25, *pn;
  cout << "n   = " << n  << endl;

  pn = &n;  // pn holds the address of n
  cout << "*pn = " << *pn << endl << endl;

  *pn = 50;

  cout << "n   = " << n   << endl;
  cout << "*pn = " << *pn << endl;

  return 0;
}
n   = 25
*pn = 25

n   = 50
*pn = 50

A pointer can point to another pointer (known as Pointers to Pointers). Let pn point to n. another pointer ppn can point to pn. In this case, *pn is an alias for n and similarly *ppn is an alias for pn. Therefore we can say that **ppn is also an alias for n. In C++, these operations can be implemented as follows.
  int n = 12;
  int *pn = &n;     // pn holds the address of n
  int **ppn = &pn;  // ppn holds the address of pn

  *pn = 25;         // now n = 25
  **ppn = 50;       // now n = 50
Pointers are used, most often, for three tasks:
  • Managing data in memory
  • Accessing functions and class member data
  • Passing variables by reference to functions

1.5 Pointer Arithmetic

When using pointers, sometimes it is required to obtain the previous or next memory locations of the address holding by the pointer. This entails the use of arithmetic operators that work on pointers. These operators are +, -, ++ and --.

Consider the following pointers that hold the address of a char, an int and a double respectively.

  char   *kar;
  int    *tam;
  double *ger;
Let the addresses held by them be 10000 (0x2710), 20000 (0x4e20) and 30000 (0x7530) respectively. Accordingly, the assignments
  kar++;
  tam++;
  ger++;
will point the values stored in the memory addresses 10001 (0x2711), 20004 (0x4e24) and 30008 (0x7538); i.e the size in bytes of the type pointed is added to the pointer. Therefore, when adding one to a pointer we make it to point to the following element of the same type with which it has been defined.

In general, to add (or subtract) the number n to a pointer p is to evaluate the address of the next (or previous) nth element of the given type pointed by the p. Examine the following assignments.

  kar++;             // equivalent to kar = kar + sizeof(char)
  tam = tam + 5;     // equivalent to tam = tam + 5*sizeof(int)
  ger = ger - 3;     // equivalent to ger = ger - 3*sizeof(double)

1.6 Pointers and Arrays

The concept of an array is very much bound to the pointer. In fact, the name of an array is a pointer and it stores the address of its first element. Because of this, any one can reach any element of an array via a pointer. Consider the following declaration.

  int mass[5], *p;
The following assignment operations are valid and equivalent
  p = mass;       // assign first element of mass to p
  p = &mass[0];   // assign first element of mass to p
Since the name of the array is a pointer, it can be assigned to a pointer of same type. In addition, according to above declaration and assignment for an int variable i, the following assignments are equivalent.
  mass[i] = 0;
  *(p+i)  = 0;

 Note that
  *p + i   // adds i to the first element 
  *(p+i)   // evaluates the address i'th block after p

01prg06.cpp: Pointers and arrays
  1:
  2:
  3:
  4:
  5:
  6:
  7:
  8:
  9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
 19:
 20:
 21:
 22:
 23:
 24:
 25:
 26:
 27:
 28:
 29:
 30:
// Pointers and arrays
#include <iostream>
using namespace std;

int main(){
  float  a[5];
  float *p;

  p = a;       // p holds the adr. of the 1st element of a
 *p = 1.5;     // that means a[0] = 1.5;

  p = &a[1];   // now p holds the adr. of the 2nd element
 *p = 2.2;     // that means a[1] = 2.2;

  p = a + 2;   // now p holds the adr. of the 3rd element
 *p = 7.1;     // that means a[2] = 7.1;

  p = a;       // now p holds the adr. of the 1st element of a
 *(p+3) = 8.3; // that means a[3] = 8.3;
 *(p+4) = 9.9; // that means a[4] = 9.9;

  cout << "  a[i]: ";
  for (int i=0; i<5; i++) cout << a[i]   << "  ";
  cout << endl;
  cout << "*(p+i): ";
  for (int i=0; i<5; i++) cout << *(p+i) << "  ";
  cout << endl;

  return 0;
}
  a[i]: 1.5  2.2  7.1  8.3  9.9
*(p+i): 1.5  2.2  7.1  8.3  9.9

1.7 Use of Pointers in Functions

We have seen the concept of arguments passed by reference to the functions in Section 6.1. We have used this idea to change external variables passed to the function inside a function. We can also use the pointers to manipulate the external variables inside the function. In this case, the address of the external variables (using the address operator &) must be supplied to the function. Examine the following swap() function.

01prg07.cpp: Swapping two values via pointers
  1:
  2:
  3:
  4:
  5:
  6:
  7:
  8:
  9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
 19:
 20:
 21:
 22:
 23:
 24:
 25:
 26:
// Swapping two values via pointers
#include <iostream>
using namespace std;

void swap(int *, int *);

int main()
{
  int x = 22, y = 33;

  cout << "Values before swapping: "
       << x << " " << y << endl;

  swap (&x, &y);

  cout << "Values after  swapping: "
       << x << " " << y << endl;

  return 0;
}

void swap(int* x, int* y){
  int z = *x;
  *x = *y;
  *y = z;
}
Values before swapping: 22 33
Values after  swapping: 33 22

One can use the relation between the pointers and arrays in function parameters. This is illustrated in the following program.

01prg08.cpp: Random array function
  1:
  2:
  3:
  4:
  5:
  6:
  7:
  8:
  9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
 19:
// Assigns integer random number [0,100] to an array of given size
#include <iostream>
#include <cstdlib>

void randomArray(int *array, int size){
  for(int i=0; i<size; i++)
    *(array + i) = rand() % 100;
}

int main(){
  int x[5];

  randomArray(x, 5);  // call the function

  for(int k=0; k<5; k++)
    std::cout << x[k] << std::endl;

  return 0;
}
83
86
77
15
93

A function may return an address. In the following program, the function maxAdr() returns the address of the maximum element of an array.

01prg09.cpp: Getting the address of the maximum element of an array
  1:
  2:
  3:
  4:
  5:
  6:
  7:
  8:
  9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
 19:
 20:
 21:
 22:
 23:
 24:
 25:
 26:
 27:
 28:
 29:
 30:
 31:
// Getting the adress of the maximum element of an array
#include <iostream>
#include <cmath>
using namespace std;

// Returns the address of the maximum element
double * maxAdr(double *array, int size){
  double  mval =  array[0];
  double *madr = &array[0];

  for(int i=1; i<size; i++){
     if(array[i] > mval) {
       mval =  array[i];
       madr = &array[i];
     }
  }
  return madr;
}

int main(){

  double  a[5] = {1.0, -2.0, 4.0, 8.0, -16.0};
  double *b;

   b = maxAdr(a, 5);

   cout << "the address of the maximum is " <<  b << endl;
   cout << "the value   of the maximum is " << *b << endl;

  return 0;
}
the address of the maximum is 0x22ff58
the value   of the maximum is 8

1.8 The NULL Pointer

A pointer whose value is zero is called a null pointer. A null pointer points to nothing. This can be used for canceling (omitting) the address of the location where it is pointed by the pointer. To do that a zero or the constant NULL can be used.

  int *p, a = 12, b;

  p = &a;     // p points to a
  p = NULL;   // (equivalent to "p = 0") p points nothing (NULL pointer)
 *p = 8       // ERROR: you cannot assign a value since p is NULL pointer

1.9 Examples

The function strCopy() in the following program makes a copy of a string.

01prg10.cpp: Making a copy of a string of type char
  1:
  2:
  3:
  4:
  5:
  6:
  7:
  8:
  9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
 19:
 20:
 21:
 22:
 23:
// Making a copy of a string of type char
#include <iostream>
using namespace std;

void srtCopy(char *, char *);

int main(){
  char x[10] = "Hello", y[10];

  srtCopy(x, y);

  cout << "x = " << x << endl;
  cout << "y = " << y << endl;

  return 0;
}

// Makes a copy of str1
void srtCopy(char *str1, char *str2){
  while(*str1 != '\0')
    *(str2++) = *(str1++);
  *(str2++) = '\0';
}
x = Hello
y = Hello

1.10 Exercises

  1. What operator is used to determine the address of a variable?
  2. What is a reference?
  3. What is a pointer?
  4. What is the difference between a reference and a pointer?
  5. What is the difference between the reference operator and address operator?
  6. What is the difference between the indirection operator and the dereference operator?

  7. What is wrong with the following code:
    int &r = 35;

  8. What is wrong with the following code:
    int* p = &45;

  9. If x has the address 0x0fffd1c, then what will values of p and q be for each of the following:
      double   x = 1.01;
      double * p = &x;
      double * q = p + 5;
    

  10. What is the output of the following program?
    #include <iostream>
    using namespace std;
    
    int main(){
      double  x = 1.7;
      double  &rx = x;
      double  *px, **ppx;
    
      px = &x; ppx = &px; **ppx = 0.5;  
      cout << x << '\t' << rx << endl;
      
      *px = 5.0*rx - 1.0;
      cout << rx << '\t' << **ppx << endl;
      
      return 0;
    }
    

  11. What is the output of the following program?
    #include <iostream>
    using namespace std;
    
    int main(){
      float  a[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
      float *p = a;
    
      for (int i=4; i>-1; i--) 
        cout << p[i] << "  ";
     
      return 0;
    }
    

  12. What is the output of the following program?
    #include <iostream>
    using namespace std;
    
    void fizik(int *, int);
    
    int main(){
     int a[] = {2, 4, -2, 5, 9};
     int n = sizeof(a)/sizeof(int);
     fizik(a, n);
     for(int i=0; i<n; i++) cout << a[i] << endl;
     return 0;
    }
    
    void fizik(int *array, int size){
      for(int j=0; j<size; ++j)
         *(array + j) *= *(array + j);
    }
    

  13. What is the output of the following program?
    #include <iostream>
    using namespace std;
    
    int * location(int a[],int n, int target){ 
      for (int i = 0; i < n; i++)
        if (a[i] == target) return &a[i];
      return NULL;
    }
    
    int main(){
      int a[8] = {22,33, 44,55,66,77,88,99}, *p, n = 44;
      p = location(a,8,n);
      if(p==NULL) cout << n << " was not found.\n";
      else cout << p << " , " << *p << endl;
       
      return 0;
    }
    
[index][.][1][2][3][4][5][6][>]
please send us you comments/questions/corrections