Computer Science I -- Week 13

Introduction

Tonight we will cover pointer and dynamic memory allocation.

Note that the links from this page are to handouts that will be distributed the night of class.  Only print out those handouts if you could not attend class.

Main topics this week:

What is a Pointer?
Declaring a Pointer
Address of Operator
Dereferencing a Pointer
Pointers with Arrays & Functions
Array Names & Pointers
Module 5 Assessment
Dynamic Memory Allocation
New & Delete
Command Line Arguments
Review for Final Exam
Next Week

What is a Pointer?

A pointer is a variable that contains the memory address of another variable.  So, for instance, if we had an integer variable that contained the value 10, then we might have a pointer that contained the memory address of where the 10 was located.  You can think of a pointer like an address of a house--the variable is the house itself, but the pointer is the address of where the house is located.

Pointers are an advanced C++ topic and are used for dynamic memory allocation.

Declaring a Pointer

A pointer is declared in the following general form:

type *name;

Where type is the data type of what the pointer points to.  So if you are declaring a pointer that can point to an integer variable, you would do:

int *ptr;

You would read this as, "ptr is a pointer to an int".

You should always initialize a pointer to something.  If you do not know what you want to put in the pointer yet, you can initialize it to 0, which means that the pointer does not point to anything (you will also see NULL used for this...NULL and 0 are the same).

int *ptr = 0;
int *ptr2 = NULL;

Address of Operator

When you want to put a value into a pointer, you need to put in the address of some other variable.  You get the address of other variables by using the address of operator (&).  For example:

int x = 5;

int *ptr = &x;

This puts the address of the variable x into the pointer ptr. 

(draw the memory diagram of what this looks like)

Dereferencing a Pointer

You can use a pointer to look at or modify what is being pointed to.  This is called dereferencing the pointer.  We dereference a pointer by using an asterisk (*).   For example:

int x = 10;
int *ptr = &x;

*ptr = 5;

cout << "x: " << x << endl;

This will display "x: 5", because *ptr is actually changing the value of what is being pointed to.  You can think of the asterisk as following the arrow on our memory diagram. 

Pointers with Arrays & Functions

Since a pointer is a data type, you can create an array of pointers.  For example:

int *array [5];

This creates an array of 5 pointers to integers.  To dereference a pointer that is in an array, get the array element and then use the asterisk:

*array [0]

You can also pass pointers to functions:

void function (int *ptr)
{
}

Array Names & Pointers

The name of an array is a pointer to the first element of the array.  So, given:

int array [5];

All of the following refer to the first element of the array:

array [0];
*array;

This means that you can use array notation [] with pointers just the same as with arrays.  But like arrays, if you try to access an element that does not exist, your program will probably crash. 

This also means that if you pass an array to a function, you can actually use a pointer as the parameter instead of an array:

void function (int *ptr)
{
}

int main ()
{
    int array [5];

    function (array);
}

Module 5 Assessment

This will be a quiz covering strings, structs, and classes.

Dynamic Memory Allocation

We've seen that arrays must always be the same size, but that vectors can grow to hold as much data as we want.  How do vectors grow?  They are able to allocate memory while the program is running, which is also called dynamic memory allocation. 

Think about an array.  You must give the size of the array when you write the code, so the memory for the array is allocated when you *compile* the program. 

For vectors, the memory is not allocated when you compile, because it doesn't know how much memory is needed when you compile.  Instead, the memory is allocated as the program is running. 

Vectors are able to do this because of the C++ keywords new and delete.

New & Delete

The keyword new is used to allocate memory.  We can allocate individual variables or we can allocate arrays of variables.  Whatever you allocate, new returns the memory location of where it was able to allocate storage.  A memory location is a pointer, so new is returning a pointer. 

For example, to allocate a single integer:

int *ptr = new int;

This dynamically allocates enough space for one integer, and returns the memory location of that space.  We can now use that integer by dereferencing the pointer.

To allocate an array, we must give it the size of the array to allocate:

int *ptr2 = new int [5];

At first glance, this looks a lot like declaring a normal array, except more complicated.  The main difference with allocating an array dynamically is that the size does *not* need to be constant.  Instead of 5 we could have put a variable that could change each time we run the program. 

Whenever you use new to allocate memory, you must also use delete to get rid of the memory when you are done with it.  There are two forms of delete, depending on whether you have allocated a single item or an array.  To delete a single item:

delete ptr;
ptr = 0;

To delete an array allocated with new:

delete [] ptr2;
ptr2 = 0;

Note that after we delete the memory pointed to by the pointers, we also set the pointer to 0.  This tells us that the pointer does not point to anything.  If we did not do this, and we tried to dereference the pointer after deleting the memory, our program would probably crash.

Here is a handout that shows using dynamic memory to allow the user to enter as many integers as they want.  Note that since we know how to use vectors, we wouldn't really use new and delete ourselves for this type of problem, but this gives you an idea of what the vector is doing for you.

Command Line Arguments

One last advanced topic is how to get command line arguments into your program.  A command line argument is something extra that is typed in on the command line when your program is started.  For example, normally to start a program called a.out you type:

>a.out

Assuming that your Unix prompt is >.  But sometimes you may want to allow the user to type in something extra, and then do something different based on what they typed in.  For example, maybe if they type in:

>a.out -?

you will want to display a message about how to use the program.  Many Unix commands work this way. 

You an respond to these extra things typed in on the command line by processing command line arguments.  Command line arguments are passed in as parameters to the main function.  If you are expecting to use command line arguments, then your main function should look like this:

int main (int argc, char *argv [])

The argc parameter is the number of command line parameters the user entered plus 1.   Command line parameters are separated by spaces.  So for the following command:

>a.out -?

The value of argc would be 2. 

The actual command line parameters themselves are stored in the argv array.  The first element in the argv array is always the name of the program that you are running.   So for the following command line:

>a.out -?

The value of argv [0] would be "a.out", and the value of argv [1] would be "-?". 

Review for Final Exam

Since next week is your presentations, tonight we will review for the final exam.   See the week 15 notes for the list of topics covered.

Next Week

Next week you will all give your presentations for your reports.