Topics to Cover

  1. Creating an array of 10 doubles
  2. Accessing each array element.
  3. Delete an array
  4. If two pointers are equal, they POINT TO THE SAME OBJECT.
  5. Given int* p = new int[n],
  6. Caveat in declaring two pointer variables at the same time.

This changes everything ...

Several weeks ago, the addition of loops to our programs had a huge impact on the power we wielded as programmers. Before loops, the computer never performed more steps than the poor programmer typed in. With loops, a short, simple program could make the computer do an almost limitless amount of work!

Today we are introducing another game-changing idea:
Arrays

Up till now, a program couldn't store -- couldn't remember -- anything except in variables. This meant that the program couldn't remember more data than the number of variables than the programmer put into the program. Essentially the program can't remember more data than the programmer typed in while creating the program.

Arrays shatter that limitation: An array is a single variable with which a program can store and retrieve a nearly limitless amount of data.

How to use an array

As described above, arrays have a different storage model, compared to the basic types such as char and int, we need a different way of using arrays.

For example, you can create and use an array of 10 ints as follows:

  1. Ask for 10 consecutive int objects in memory.
  2. Have a variable that points to the first of these.
  3. From this pointer variable you access a particular one of the 10 ints by index.

Allocating space for an array: new operator

In any event, having declared p as a pointer to objects of type int, we have to actually allocate some ints in memory for it to point to! Just declaring p leaves it uninitialized - i.e. pointing to nothing, or what's worse, pointing to some random area of memory.

For example, to allocate 10 ints in memory and set p to point to the first of them, we'd write:


int* p;                 // delcare a pointer p 
p = new int[10];        // p points to a newly allocated array of 10 ints
Syntax: You allocate memory with the new operator using the following sytax:


new type[number]

This expression allocates number consecutive objects of type type in memory, and returns a pointer to the first object.

Accessing the items of an array: index

Consider the following code snippet again:

int* p;                 // delcare a pointer p 
p = new int[10];        // p points to a newly allocated array of 10 ints

Important: When you are indexing in an array...

At this point, we can access any of these int objects by the indices 0,1,2,3,4,5,6,7,8,9 just as we did for the characters within a string. For example:

In summary:

The following picture depicts what's going on when we create an array of 4 ints and assign a value to one of the ints in the array.

DECLARE
VARIABLES
Notes on scope:

The pointer is a variable with name and scope like we're used to. That is, the pointer p is a local variable of main, and is part of that function call record on the stack.

ALLOCATE
SPACE
Notes on scope:

However, the array that pointer points to is in a different area of the program's memory called, the heap.

The array itself is not part of any function call record on the stack. That's what this picture is showing you.

ASSIGN
A VALUE

A Simple Problem

Let's look at a simple problem that we can solve using arrays:

Write a program that works as follows:
  1. Reads in a number n from the user
  2. Reads in n doubles from the user
  3. Then, simply prints them out in reverse order.

We already saw from our exercises with accessing characters within a string that indexed collections of objects would allow us to print objects in reverse order.


int main()
{
  // Get number of doubles from user
  int n;
  cout << "How many doubles? ";
  cin >> n;

  // Construct array
  double* A;
  A = new double[n];

  // Read numbers
  cout << "Enter " << n << " doubles: ";
  for(int i = 0; i < n; i++)
    cin >> A[i];

  // Write in reverse order
  cout << "In reverse your numbers are: ";
  for(int j = n - 1; j >= 0; j--)
    cout << A[j] << " ";
  cout << endl;

  return 0;
}
Even an example as simple as this deserves a fair bit of commentary the first time around. In class we'll go over it in painful detail, including pictures.

The variable is just a pointer! Not the array itself!

A pointer variable contains only a number -- an address (like a GPS location) of the object it points to.
You must keep the following in your heart. We expect you to be able recite them like a mantra without looking.

What's hopefully clear here is that by creating a new pointer q and setting q = p, we did not create a new array populated with the same integer values. We just created a new pointer and set it to point to the same old array.

Quick check

Give the output of the following code:

int* pa = new int[3];
int* pb = new int[3];
pa[0] = 1;  pa[1] = 2;  pa[2] = 3;
pb[0] = 6; pb[1] = 7; pb[2] = 8;

int* q;
q = pb;
q[1]++;
cout << pa[1] << " " << pb[1] << " " << q[1] << endl;

q = pa;
q[1]++;
cout << pa[1] << " " << pb[1] << " " << q[1] << endl;
Answer (drag your mouse):
2 8 8
3 8 3

Objects allocated with new have no scope and live until you kill them

One of the funky things about objects allocated with new is that they have no scope.

So, for example, if you wrote a function that called new to allocate some space, the objects in memory created by new would not be destroyed when the function returned, even though the pointer we had goes out of scope.

Take this example:

Before the function call
During the function call
After the function call

Destroying an array: delete []

You can see that after the function call, our array is still there - although it's utterly useless, since we no longer have a pointer with which to access the array. Instead of being destroyed automatically by scoping rules, objects allocated with new either stick around until the end of the program, or are explicitly destroyed with the delete operator.

For example:


char* p;
p = new char[30]; 	// creates 30 consecutive char's in memory
delete [] p;        // destroys the objects created by new
We'll worry more about delete later.

A note on types

We now have a new type: "pointer to X", where X is a type. So, for example, with the declaration

bool* B = new bool[10];
The type of B is bool*, and the type of B[0] is bool.

If we have the declaration


string* A = new string[20];
The type of A is string*, and the type of A[0] is string.

Each element of an array is an lvalue.

It's also important to note that, as we saw with strings, a subscripted array expression like A[5] is an lvalue, i.e. it is allowed on the left-hand side of an assignment. This is tremendously important.

This means that A[5] is not a copy of the index-5 element of the array, it is the actual index-5 element. You can modify it. For example:

Quick check

Consider the following code:

string* A = new string[20];
A[5] = "hello";
Give the type of each expression below (drag your mouse to check answers):
A string*
A[5] string

Declaring multiple pointer variables at the same time

When declaring two pointer variables, the declaration syntax is a little weird.
wrong correct recommended

int* p, q;    

int *p, *q;   

int* p;      
int* q;
For example, the statement on the left in the above table declares p to be of type int* and q to be of type int. So the int applies to all the variables, but the * part only applies to the next variable.

If you wanted both p and q to be "pointers to ints", you'd have to write


int *p, *q;   // now you are declaring two pointers!!

This is unfortunate; it's just because the syntax of C++ is inherited from that of C. For this reason, when it comes to pointers, we recommend that you always declare a single pointer variable.

Mandatory Practice Problems

  1. Write a program to compute dot-products of vectors. If v = [a1,a2,...,am] and w = [b1,b2,...,bm] are two vectors of dimension m, the dot product of v and w is a1*b1 + a2*b2 + ... + am*bm. Your program will get a dimension m from the user, read in two vectors of length m, and print out their dot product. Here's a sample run:
    Enter dimension: 4
    Enter two vectors: 
    [3,-1,0,2]
    [5,0,9,-7]
    Dot product = 1
    Try it for yourself and then take a look at my solution.

Other Practice Problems

  1. Write a program that reads a list of banned words from a file, stores them in an array, and then simply reads words from the user and returns "banned" or "not banned" until the word "end" is encountered. The file starts with a number, which is the number of banned words, and then the words themselves are listed. The file banned.txt is a good example. Here's my solution.