C++ Pointers

GitHub Code for these examples

A pointer is a variable that stores the memory address of an object or variable.
Pointers are used extensively in both C and C++ for three main purposes:

  • to allocate new objects on the heap,
  • to pass functions to other functions.
  • to iterate over elements in arrays or other data structures.

Repls for this code is here and here.

Example 1: Simple Pointer

Here we are creating a pointer to an interger. pi will contain the address in memory of where x is stored

// A simple pointer
int *pi=NULL;    // A pointer to an integer
int x = 42; // an integer
pi = &x;    // copy the address of x into pi
cout << "pi:" << *pi << endl;   // print what pi points to
cout << "pi:"<< pi << endl;    // Print out the address

“*” – Means use this value as a pointer somewhere else, or “dereference pointer”
“&” – Means return the address of this variable or object.

Example 2: Creating objects on the heap

If we want to create something in a function and have it available outside the function, we create it on the heap. The heap is a global data structure.

  // Create an integer on the heap
  int *pi2 = new int;
  *pi2 = 53;
  if (pi2 != NULL) {      // It's a good idea to check pointer
    cout << *pi2 << endl; 
  }
  delete pi2;
  cout << *pi2 << endl;    // Bad - pi2 pointer no longer valid
  pi2 = NULL;     // Should set pointer to NULL after deleting

  // Initialize on creation
  int *pi3 = new int(76);
  cout << *pi3 << endl;
  

Example 3: Dynamic variables and pointers, e.g. pointers are dangerous!

Local variables defined in a function are created when the function are called, and are destroyed when the function exits. (These variables are actually placed on a “runtime stack”). We have to be careful when pointing to them, because they may no longer be valid. This is one of the reasons pointer are considered dangerous.

#include <iostream>
using namespace std;

// Return a pointer to int created on stack
int* right() {
  int *pn = new int(23);
  return pn;
}
// Return a pointer to int created on heap
int* wrong() {
  int n = 23;
  return &n;
}

int main() {

  // Get an int created in function
  int *p1 = wrong();
  cout << "Wrong: " << *p1 << endl;
  int *p2 = right();
  cout << "Right: " << *p2 << endl;
  
}

Example 4: Pointers to object

It is very common to create objects on the heap, and then use pointers to the objext. This allows (as we saw above) us to create ojects the persist beyond the life of the routine they are created in.

When we create an object with a new, we get a pointer. We must then use the “->” operator rather then the “.” operator to access the members.

  Point *p1 = new Point(5,5);
  cout<<"Point1 (x,y) is: ("<<p1->GetX()<<","<<p1->GetY()<<")"<<endl;
  cout<<endl;
  Point *p2 = new Point(4,4);
  cout<<"Point2 (x,y) is: ("<<p2->GetX()<<","<<p2->GetY()<<")"<<endl;
  cout<<endl;

You must be careful to dereferance the pointers property when you use them, or you may not get what you really want. This is because when operators are overloaded, it will use the operator that matches the value. Since a pointer is link an int, you may use the int operator rather then the closs operator:

// Below is not right, why? 
  if (p1==p2) {
    cout << "Error - Point1 should not be equal to Point2" << endl;
  } else {
    cout << "Point1 is not be equal to Point2" << endl;
  }
  // Comparing pointers
  if (p2==p3) {
    cout << "Point2's pointer is not equal to Point3's pointer !" << endl;
  } else {
    cout << "Different pointers" << endl;
  }
  // Comparing the object values
  if (*p2==*p3) {
    cout << "Point2 pointer is  equal to Point3!" << endl;
  } else {
    cout << "Different pointers" << endl;
  }

  double dist1 = p2-p1;  // Not right - would subtract the pointers
  cout << "The distance from Point1 to Point2 is " << dist1 << endl;
  double dist2 = p2-p1;
  cout << "The distance from Point2 to Point1 is " << dist2 << endl;    

  double dist3 = *p2-*p1;  // Not right - would subtract the pointers
  cout << "The distance from Point1 to Point2 is " << dist3 << endl;
  double dist4 = *p2-*p1;
  cout << "The distance from Point2 to Point1 is " << dist4 << endl;    
  cout <<endl<<endl<<endl<< "END!!!" << endl;

Example 5: An array of object

Sometime we want to store many objects. An array can do the trick:

// Create n random points
Point* createNRandPoints(int n) {
  #define MAX 100
  Point *points = new Point[n];

  for (int i=0;i<n; i++) {
    points[i].SetX(rand() % MAX);
    points[i].SetY(rand() % MAX);
  }

  return points;
}

Example 6: An array of pointers to an object

Sometime we need to store many objects, but we want to point to objects rather then store objects. This allows us to pass arounds raferences to objects An array of object pointers can do the trick:

// Create n random points
// As a list of pointers to point objects
Point** createNRandPointsPtr(int n) {
  #define MAX 100
  Point **points = new Point*[n];

  for (int i=0;i<n; i++) {
    points[i] = new Point(rand() % MAX,rand() % MAX);
  }
    return points;
}

More on pointer risks

C++ does not manage pointers safely. For example:

  1. When you create a pointer without initializing it, it points somewhere random.
  2. If you try to use a NULL pointer, your program will crash. You should always test pointers before you trust them.
  3. If you create an object with new and have a pointer to it, when you delete the object the poiter is not chaged. It points into the heap at where the object used to be valid. The contents may still be there, fooling you, or yous may be pointing to a different object (very dangerous). This is called a dangling pointer.
  4. You an have more then one pointer to an object, and the object is deleted, all the pointers are now dangling. This can be hard to keep track off.
  5. If you point to an object that is created as a local variable, then return a pointer to the object when you exit the function, your will also have a dangling pointer.
  6. If you create somethinbg with a new, and later change the pointer to something else, you have a memory leak. This means you have an object stored in the heap you have lost access too, and it just takes up memory.

Rust is a newish language like C++ in syntax, but employs “smart pointers”. These types of pointer are considered safe, and the programming language doesn’t let the program compile if you try to do somtething with a pointer that is dangurous. It also has features that automatically fix pointers so that is it very hard tohave dangling pointers or memory leaks.

Java is a popular C++-like lnanguge the provides both object pointers and object references. References are more controlled, and hard to break. Also, object can be created by new, but there is no delete. Java keeps track of objects in the heap, and when they are not longer accessable, the runtime system “garbage collects” the memroy, puting it back into circulation.

Scroll to Top