Memory looks like a very long CD rack. Every slot in the rack consists of one byte (a set of 8 bits). Every byte has a number called its address.
The address of a variable is the address of the first byte in the variable.
The following code should be self explanatory:
int A = 5; cout << "The value of A is " << A << endl; cout << "The address of A is " << &A << endl;
The output of this code would be 5 - the value of A, and a hex number that is the memory address of A.
In summary: &A is the address of A.
A code fragment:
int A, *Aptr; //Aptr is a pointer to an int type variable A = 5; Aptr = &A; //Stores the address of A in Aptr cout << "A is at location " << Aptr << endl; cout << "Aptr points to an int with value " << *Aptr << endl;
Notes:
The output of the code fragment will be:
A is at location < a hex address >
Aptr points to an int with value 5.
In summary:
As seen above, *Aptr is the value of the variable that Aptr points to. Also one can do this:
int A, *Aptr; Aptr = &A; //Aptr gets the address of A *Aptr = 5; //5 is placed in the variable Aptr points to cout << A << endl;
5 will be printed.
The line *Aptr = 5 puts the 5 in the variable that Aptr points to, that is A.
In summary: For all practical purposes, after Aptr = &A; *Aptr is the same as A.
int A, *Aptr, **whatIsThis;
A = 6; Apt = &A; whatIsThis = &Aptr; cout << A << endl << Aptr << endl << *Aptr << endl << whatIsThis << endl << *whatIsThis << endl << **whatIsThis << endl;
char name [10] = "Harvey";
makes memory look like this:
| name | H | a | r | v | e | y | 0 | |||
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
But this is also possible:
char name [] = "Harvey";
which makes memory look like this:
| name | H | a | r | v | e | y | 0 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 |
This gets kind of tedious if there are many names. But C++ lets us do this:
char *names [3] = {"Harvey", "Myrtle", "Smedley"};
cout << names [0] << endl
<< names [1] << endl
<< names [2] << endl;
The output is:
Harvey
Myrtle
Smedley
Here is another piece of code:
char name [] = "Harvey"; char *position = &name [2]; cout << name << endl; cout << position << endl;
What gets printed? This:
Harvey
rvey
Here's what goes on:
In summary:
int nums [5] = {9,4,2,7,5};
int *numPtrs [5];
numPtrs [0] = &nums [4];
numPtrs [1] = &nums [1];
numPtrs [2] = &nums [3];
numPtrs [3] = &nums [0];
numPtrs [4] = &nums [2];
cout << numPtrs [0] << endl;
cout << *numPtrs [1] << endl;
cout << *numPtrs [2] << endl;
A piece of code:
char name [] = "Harvey";
char *namePtr, *otherPtr;
namePtr = name;
otherPtr = name + 3;
cout << name << endl;
cout << namePtr << endl;
cout << otherPtr << endl << endl;
for (int offset = 0; offset < 6; offset++)
{
otherPtr = name + offset;
cout << otherPtr << endl;
}
The output is
Harvey
Harvey
vey
Harvey
arvey
rvey
vey
ey
y
What is happening here?
A code fragment:
int x[7] = {1,2,3,4,5,6,7};
int *xPtr;
xPtr = x + 3;
cout << *xPtr << endl;
cout << *(xPtr + 2) << endl;
The output is
4
6
Note here that x + 3 advances the address in xPtr by 3 int length (12 bytes on most systems) and not by 3 bytes.
In summary: If ptr points to slot n of an arry, then ptr + 3 points to slot n + 3 of the array.
int x [10] = {2,1,5,7,8,4,3,6,9};
int *ptr = &x [4];
Write C++ statements using the pointer prt to
char A [10] = "Mortimer"; char *ptr = &A [2];
Write C++ statements that use ptr to
A code fragment:
int x [5] = {2,4,6,8,10};
int *xPtr;
xPtr = &x [2];
cout << xPtr [0] << endl;
cout << xPtr [1] << endl;
cout << xPtr [2] << endl;
Here is what is printed:
6
8
10
What goes on here?
In summary: If Aptr is a pointer, then Aptr [n] is the same as *(Aptr + n).
int *ptr, A [5] = {6,8,3,2,1};
ptr = &A [1];
cout << ptr [3] << endl
<< ptr [2] << endl
<< ptr [0] << endl;
What is printed?
char name [] = "George Washington"; char *one, *two; one = name; two = &name [7];
What further lines must be added to get output like this:
G
e
o
r
g
e
W
a
s
h
i
n
g
t
o
n
Stack memory (or the stack) is the area of memory where the computer stores values for variables that have been set up in declarations, stores function calls, and stores the arguments of function calls. Stack memory is limited in size and is largely out of the programmer's control. (Actually, you can use operating system calls to change the amount of stack memory, but how you do this will vary from operating system to operating system.)
Free store is the part of memory that is available for the program to stake out as its own. Another name for free store is heap memory (or the heap).
These lines take int storage (4 bytes on most compilers) and put 5 in that storage space:
int *x = NULL; //Set up x, a pointer to int, initialize it to NULL. x = new int (5); //Reserve space for an int in free store, // put the address of the first byte in the pointer x, // and store 5 int the space x points to.
These lines set up an array of 5 ints in free store:
int *x = NULL; //Set up x, a pointer to int, initialize it to NULL.
x = new int [5]; //Set up an array of 5 ints in free store.
if (x != NULL) //If the new failed (free store space not available)
// then new will return NULL to x. Otherwise, store
// values in the int array.
{
x [0] = 6;
x [1] = 4;
x [2] = 5'
}
Finally, these lines set up a string in free store:
char *str = NULL;
int length = strlen ("George");
str = new char [length + 1] //One extra space for the null
// char at the end of the
// string.
if (str!= NULL)
{
strcpy (str, "George");
cout << str;
}
If you have reserved memory with new, then this memory is not automatically released when your program ends. The memory you have reserved is released only when you release it with the delete operator.
If x is a pointer to a single int, then you can release the 4 bytes of memory with delete x; If str is a pointer to a character array, then you can release the memory with delete []str;
This short program reserves memory for a string, stores the string, prints it, and then releases the memory.
#include <iostream.h>
#include <string.h>
int main ()
{
char *str = NULL;
int length = strlen ("George");
str = new char [length + 1];
if (str != NULL)
{
strcpy (str, "George");
cout << str;
}
delete []str; //Release the memory reserved in the new above.
str = NULL; //Re-initialize str for saftey's sake.
return 0;
}
Here are several code fragments that will result in an almost certain system crash:
int *x;
delete x;
Here x doesn't point to anything, so the delete will fail catastrophically. This is the reason for initializing x to NULL. delete x does nothing if x is equal to NULL.
int *x;
x = new int (5);
cout << x;
delete x;
x = NULL;
(*x) = 4;
After the delete line, the storage that x points to no longer exists.
int *x;
x = new int (5);
cout << x;
delete x;
delete x;
We can't release the storage that x points to twice.
If a class has pointer data in it, then you have to be careful when one object of the class is copied or created from another.
The problem is that, when you copy a pointer of one object into another object, the copy points to the same block of memory that belongs to the first object. New memory is not automatically created for the second object.
So, whenever a class includes a pointer as a data member, the class should have
If either the copy constructor of the operator= are missing, then lots of obscure problems may arise.
Suppose we want to make a class to represent a list of ints in such a way that the list is no longer than it has to be. In fact, we would like it to figure out the amount of storage on the fly.
Such a class might be defined like this:
class intList
{
public:
intList (); //Default constructor
intList (int aList [], int howMany);
intList (intList&); //Copy constructor
~intList (); //Destructor
void operator= (intList&); //Overloads =
void inputNumbers ();
void print ();
double average ();
private:
int *theList; //Pointer to point to a dynamic array.
int size; //Size of the dynamic array.
};
Notes:
Such a function is called a destructor. Note: You can't call the destructor yourself. The computer will call it.
Here are the first two constructors:
intList::intList () //Default constructor
{
size = 0;
theList = NULL;
}
intList::intList (int aList [], //Incoming list of numbers
int howMany) //How many numbers are incoming
{
size = howMany; //Copy over the list size
theList = new int [size]; //Create a dynamic array of the right
// size. theList points to the 0
// slot of the array.
for (int n = 0; n < size; n++) //Copy the numbers over to the dynamic
theList [n] = aList [n]; // array.
}
Notes:
A copy constructor is a constructor that makes the object as a copy of another object of the same type. In this case the copy constructor for the intList class makes a new intList object as a copy of a given intList object.
Why should we care about copy constructors? Any class that has pointers in its data must have a copy constructor to enable proper copying. If it doesn't, then the pointer of the new object will end up pointing to the dynamic memory of another object, a bad state of affairs.
But the copy constructor is not called if you use = . The copy constructor is called when an object is returned as the return type of a function. The copy constructor is also called when an object is used as a by-value argument in a function.
Here is the copy constructor for the intList class:
intList::intList (intList& aList) //The argument is an object of
// the same type, to be copied.
{
size = aList.size; //Copy over the size of the list.
theList = new int [size]; //Make an array of the proper size.
for (int n = 0; n < size; n++) //Copy over the numbers.
theList [n] = aList.theList [n];
}
Notes:
This function will look like:
void intList::operator= (intList& aList)
{
size = aList.size; //Copy over the size of the list.
theList = new int [size]; //Make an array of the proper size.
for (int n = 0; n < size; n++) //Copy over the numbers.
theList [n] = aList.theList [n];
}
In the class definition for intList, the prototype ~intList (); indicates a destructor: a function that is called by the computer (and not by the program) when any intList object goes out of existence.
What needs to be done in a destructor? Any cleaning up, especially releasing back to free memory any memory that the program grabbed.
The intList destructor might look like this:
intList::~intList ()
{
delete []theList;
}
The effect of this destructor is to release the memory attached to the pointer theList. Note that it is important that theList is first initialized to NULL. In this case delete will work all right even if no memory gets attached to theList. Otherwise a crash will result.
The intList.h file:
#ifndef INTLIST_H
#define INTLIST_H
class intList
{
public:
intList (); //Default constructor
intList (int aList [], int howMany);
intList (intList&); //Copy constructor
~intList (); //Destructor
void operator= (intList&); //Overloads =
void inputNumbers ();
void print ();
double average ();
private:
int *theList; //Pointer to point to a dynamic array.
int size; //Size of the dynamic array.
};
#endif
The intList.cpp file:
#include <iostream>
#include <iomanip>
#include "intList.h"
const int MAXSIZE = 10000;
intList::intList () //Default constructor
{
size = 0;
theList = NULL;
}
intList::intList (int aList [], //Incoming list of numbers
int howMany) //How many numbers are incoming
{
size = howMany; //Copy over the list size
theList = new int [size]; //Create a dynamic array of the right
// size. theList points to the 0
// slot of the array.
for (int n = 0; n < size; n++) //Copy the numbers over to the dynamic
theList [n] = aList [n]; // array.
}
intList::intList (intList& aList) //The argument is an object of the
// same type, to be copied.
{
size = aList.size; //Copy over the size of the list.
theList = new int [size]; //Make an array of the proper size.
for (int n = 0; n < size; n++) //Copy over the numbers.
theList [n] = aList.theList [n];
}
intList::~intList () //Destructor
{
delete []theList;
}
void intList::operator= (intList& aList)
{
size = aList.size; //Copy over the size of the list.
theList = new int [size]; //Make an array of the proper size.
for (int n = 0; n < size; n++) //Copy over the numbers.
theList [n] = aList.theList [n];
}
void intList::inputNumbers ()
{
cout << "Enter numbers. Enter -1 to stop." << endl << endl;
//Enter the numbers into a temporary array first.
int size = 0;
int tempList [MAXSIZE];
cin >> tempList [size];
while (tempList [size] != -1)
{
size++;
cin >> tempList [size];
}
//Then create a dynamic array of just the right size.
theList = new int [size];
//And copy the numbers into the dynamic array.
for (int n = 0; n < size; n++)
theList [n] = tempList [n];
}
void intList::print ()
{
for (int n = 0; n < size; n++)
cout << setw (10) << theList [n] << endl;
}
double intList::average ()
{
int sum = 0;
for (int n = 0; n < size; n++)
sum += theList [n];
return double (sum) / double (size);
}