Chapter 10
Pointers and
Dynamic Arrays
Copyright © 2017 Pearson Education, Ltd.
All rights reserved.
Pointer Introduction
• Pointer definition: memory address of a variable
• You’ve used pointers already: Call-by-reference parameters
• Pointers are "typed“ and declared like other types
– Can store pointer in variable, not int or double but a POINTER to int, double, etc
– Adding "*" before variable name produces "pointer to" that type
double *p1, v1;
int *p2, v2;
– p1 is declared a "pointer to double" variable
• can hold pointers to variables of type double only.
– p2 hold pointers to int variables
– v1 is ordinary double variable
– v2 is ordinary int variable
10-2
Pointing
int *p, v; 42
p = &v;
– Sets pointer variable p to "point to" int variable v
– p and v refer to same variable
• Operator & determines "address of" variable
• Read like: "p equals address of v“ or "p points to v “
• Two ways to refer to v now: v = 0;
p = &v;
– Variable v itself: cout << v; *p = 42;
cout << v << endl;
– Via pointer p: cout *p; cout << *p << endl;
• Dereference operator, * • Produces output:
42
– Pointer variable "derereferenced" 42
– Means: "Get data that p points to"
10-3
Pointer Assignments
• Pointer variables can be "assigned":
– Assigns one pointer to another
int *p1, *p2;
– "Make p1 point to where p2 points" p1 = p2;
• Do not confuse with: *p1 = *p2;
– Assigns "value pointed to" by p1, to "value pointed to" by p2
10-4
Memory Management
• Stack: special memory region
– stores temporary variables created by each function,
– Automatically managed by the OS/CPU.
– Every time a function declares a new variable, it is
"pushed" onto the stack.
– When a function exits, all variables that it pushed
onto the stack, are deleted.
• Heap: memory region reserved for dynamically-allocated variables
– Also called "freestore"
– is not managed automatically and is not as tightly managed by the CPU.
– To allocate memory on the heap, you must use new operator.
– you responsibility to free that memory once you don't need it any more
• you must use delete operator to destroy/free/desallocate memory
• If you fail to do this, your program will have what is known as a memory leak.
10-5
– Future "new" operations will fail if freestore is "full“
The new Operator
• Can dynamically allocate variables
– new creates dynamic variable and returns pointer to the new variable
• int *P1;
p1 = new int;
– Creates new "nameless" variable, and assigns p1 to "point to" it
– access content with *p1 and use just like ordinary variable
• If type is class type: MyClass *mcPtr;
mcPtr = new MyClass(32.0, 17);
– Constructor is called for new object
– Can invoke different constructor with initializer arguments
• Can still initialize non-class types:
int *n;
n = new int(17); //Initializes *n to 17
10-6
Basic Pointer Manipulations Example:
#include <iostream>
using namespace std;
int main( ){
int *p1, *p2;
p1 = new int;
*p1 = 42;
p2 = p1;
cout << "*p1 == " << *p1 << endl;
cout << "*p2 == " << *p2 << endl;
*p2 = 53;
cout << "*p1 == " << *p1 << endl;
cout << "*p2 == " << *p2 << endl;
p1 = new int;
*p1 = 88;
cout << "*p1 == " << *p1 << endl;
cout << "*p2 == " << *p2 << endl;
cout << "Hope you got the point of this example!\n";
return 0;
} *p1 == 42
*p2 == 42
*p1 == 53
*p2 == 53
*p1 == 88
*p2 == 53
10-7
Hope you got the point of this example!
Checking new Success
• Test if null returned by call to new:
– If new succeeded, program continues
int *p;
p = new int;
if (p == NULL) // NULL represents empty pointer
{
cout << "Error: Insufficient memory.\n";
exit(1);
}
– If new fails: Newer compilers terminate program automatically
• Produces error message
• Still good practice to use NULL check
10-8
delete Operator
• De-allocate dynamic memory using delete operator:
– When no longer needed int *p;
p = new int(5);
– Returns memory to freestore
… //Some processing…
– Literally "destroys" memory
delete p;
p = NULL;
• delete p; // Destroys dynamic memory
– But p still points there! : "dangling pointer"
– If p is then dereferenced ( *p ) is unpredicatable!
– Avoid dangling pointers by assign pointer to NULL after delete
• Dynamic variables: Created with new operator and destroyed with
delete while program runs (you have to maintain them)
• Local or automatic variables: Created when function is called and
destroyed when function call completes
10-9
Define Pointer Types
• Can "name" pointer types to be able to declare pointers
like other variables
– Eliminate need for "*" in pointer declaration
typedef int* IntPtr; //defines a "new type" alias
– Now these declarations are equivalent :
IntPtr p;
int *p;
Copyright © 2017 Pearson Education, Ltd. All rights reserved. 10-10
Call-by-value Pointers Example:
//Program to demonstrate the way call-by-value parameters
//behave with pointer arguments.
#include <iostream>
using namespace std;
typedef int* IntPointer;
void sneaky(IntPointer temp);
int main( ){
IntPointer p;
p = new int;
*p = 77;
cout << "Before call to function *p == " << *p << endl;
sneaky(p);
cout << "After call to function *p == "<< *p << endl;
return 0;
}
void sneaky(IntPointer temp) {
*temp = 99;
cout << "Inside function call *temp == "<< *temp << endl;
10-11
}
Array Variables and pointer variable
• Array variables: really pointer variables!
– arrays stored in memory addresses, sequentially
– Array variable "refers to" first indexed variable, so it is a pointer!
• array pointer is CONSTANT pointer!
• Can perform assignments but be careful
int a[10];
typedef int* IntPtr;
IntPtr p; //a and p are both pointer variables!
p = a; // Legal. p now points where a points:
// To first indexed variable of array a
a = p; // ILLEGAL! Array pointer is CONSTANT pointer!
• Function that Returns an Array
– Array type NOT allowed as return-type of function
– Example: int [] someFunction(); // ILLEGAL!
– Instead return pointer to array : int* someFunction(); // LEGAL!
10-12
Arrays and Pointer Variables
//Program to demonstrate that an array variable is a kind of pointer variable.
#include <iostream>
using namespace std;
typedef int* IntPtr;
int main( ){
IntPtr p;
int a[10];
int index;
for (index = 0; index < 10; index++)
a[index] = index;
p = a;
Note that changes
for (index = 0; index < 10; index++)
to the array p
cout << p[index] << " ";
are also changes
cout << endl; to the array a.
for (index = 0; index < 10; index++)
p[index] = p[index] + 1;
for (index = 0; index < 10; index++)
cout << a[index] << " ";
cout << endl;
return 0;
0123456789
}
1 2 3 4 5 6 7 8 9 10 10-13
Dynamic Arrays
• Standard array: Fixed size that is specified when it is declared
• Dynamic array : Size not specified at programming time but
determined while program running
– Created using new operator typedef double * DoublePtr;
– Treated like standard arrays DoublePtr d;
d = new double[10]; //Size in
brackets
… //Processing
delete [] d;
d = NULL;
• Deleting Dynamic Arrays: should be destroyed at run-time
– delete [] d de-allocates all memory for dynamic array
– Brackets indicate "array" is there
– Recall: d still points there!
• Should set d = NULL;
10-14
#include <iostream>
using namespace std;
typedef int* IntPtr;
Dynamic
void fillArray( int a[], int size);
int search( int a[], int size, int target);
Arrays
int main( ) {
cout << "This program searches a list of numbers.\n";
int arraySize;
cout << "How many numbers will be on the list? ";
cin >> arraySize;
IntPtr a; This program searches a list of numbers.
a = new int[arraySize]; How many numbers will be on the list? 5
fillArray(a, arraySize); Enter 5 integers.
int target; 12345
cout << "Enter a value to search for: "; Enter a value to search for: 3
cin >> target;
3 is element 2 in the array.
int location = search(a, arraySize, target);
if (location == -1) cout << target << " is not in the array.\n";
else cout << target << " is element " << location << " in the array.
n";
delete [] a;
return 0;
}
void fillArray( int a[], int size){
cout << "Enter " << size << " integers.\n";
for ( int index = 0; index < size; index++) cin >> a[index];
}
int search( int a[ ], int size, int target) {
int index = 0;
while ((a[index] != target) && (index < size)) index++;
if (index == size) index = -1; //if target is not in a. 10-15
return index;
Pointer Arithmetic
• Can perform arithmetic on pointers: "Address" arithmetic
typedef double* DoublePtr;
– d contains address of d[0] DoublePtr d;
– d + 1 evaluates to address of d[1] d = new double[10];
– d + 2 evaluates to address of d[2]
• Equates to "address" at these locations
• Alternative Array Manipulation
– "Step thru" array without indexing:
for (int i = 0; i < arraySize; i++)
cout << *(d + i) << " " ;
– Equivalent to:
for (int i = 0; i < arraySize; i++)
cout << d[i] << " " ;
– Only addition/subtraction on pointers : No multiplication, division
– Can use ++ and -- on pointers
10-16
Back to Classes and this pointer
• The -> operator : shorthand notation
MyClass *p;
p = new MyClass;
p->grade = "A"; //Equivalent to: (*p).grade = "A";
– Combines dereference operator, *, and dot operator
– Specifies member of class "pointed to“ by given pointer
• Member function definitions might need to refer to calling object
– Use predefined this pointer which automatically points to calling object:
Class MyClass{
public:
void showStuff() const;
private:
int stuff;
};
• Two ways for member functions to access:
cout << stuff; or cout << this->stuff;
10-17
Overloading Assignment Operator
• Assignment operator returns reference
– So assignment "chains" are possible
– e.g., a = b = c;
• Sets a and b equal to c
• Operator must return "same type" as it’s left-hand side
– To allow chains to work
– The this pointer will help with this!
• Assignment operator when overloaded must be member of the class
– It has one parameter
– Left-operand is calling object
s1 = s2;
• Think of like: s1.=(s2);
• s1 = s2 = s3;
– Requires (s1 = s2) = s3;
– So (s1 = s2) must return object of s1"s type
• And pass to " = s3";
10-18
//Objects of this class are partially filled arrays of doubles.
class PFArrayD {
public: Parray.h
PFArrayD( );
//Initializes with a capacity of 50.
PFArrayD( int capacityValue);
PFArrayD( const PFArrayD& pfaObject); //copy constructor
void addElement( double element);
//Precondition: The array is not full.
//Postcondition: The element has been added.
bool full( ) const { return (capacity == used); }
//Returns true if the array is full, false otherwise.
int getCapacity( ) const { return capacity; }
int getNumberUsed( ) const { return used; }
void emptyArray( ){ used = 0; }//Empties the array.
double& get( int index); //get reference of a [index]
//double& operator[]( int index); // replace get by overload []
//Read and change access to elements 0 through numberUsed - 1.
PFArrayD& operator =( const PFArrayD& rightSide);
~PFArrayD( );
private:
double *a; //For an array of doubles
int capacity; //For the size of the array
int used; //For the number of array positions currently in use
10-19
};
#include <iostream>
using namespace std;
#include "parray.h" Parray.cpp
PFArrayD::PFArrayD( ) :capacity(50), used(0){
a = new double[capacity];
}
PFArrayD::PFArrayD( int size) :capacity(size), used(0){
a = new double[capacity];
}
PFArrayD::PFArrayD( const PFArrayD& pfaObject)
:capacity(pfaObject.getCapacity( )), used(pfaObject.getNumberUsed( )){
a = new double[capacity];
for ( int i = 0; i < used; i++)
a[i] = pfaObject.a[i];
}
void PFArrayD::addElement( double element){
if (used >= capacity){
cout << "Attempt to exceed capacity in PFArrayD.\n";
exit(0);
}
a[used] = element;
used++;
}
10-20
double& PFArrayD::get( int index){
if (index >= used){
cout << "Illegal index in PFArrayD.\n"; Parray.cpp
exit(0);
}
return a[index];
}
PFArrayD& PFArrayD::operator =( const PFArrayD& rightSide){
if (capacity != rightSide.capacity){ //make sure left array capacity
delete [] a; // is same as right
a = new double[rightSide.capacity];
}
capacity = rightSide.capacity;
used = rightSide.used;
for ( int i = 0; i < used; i++)
a[i] = rightSide.a[i];
return * this; //this refers to the invoking object => left operand
}
PFArrayD::~PFArrayD( ){
delete [] a;
}
10-21
#include <iostream>
using namespace std;
#include "parray.h" parrayDemo.cpp
void testPFArrayD( );
//Conducts one test of the class PFArrayD.
int main( )
{
cout << "This program tests the class PFArrayD.\n";
char ans;
do
{
testPFArrayD( );
cout << "Test again? (y/n) ";
cin >> ans;
} while ((ans == 'y') || (ans == 'Y'));
return 0;
}
To compile : g++ .\parray.cpp .\parraydemo.cpp -o myprogram
Or
g++ -c .\parray.cpp
g++ -c .\parraydemo.cpp
g++ .\parray.o .\parraydemo.o
10-22
void testPFArrayD( ){
int cap; parrayDemo.cpp
cout << "Enter capacity of this super array: ";
cin >> cap;
PFArrayD temp(cap);
cout << "Enter up to " << cap << " nonnegative numbers.\n";
cout << "Place a negative number at the end.\n";
double next;
cin >> next;
while ((next >= 0) && (!temp.full( ))){
temp.addElement(next);
cin >> next; This program tests the class PFArrayD.
} Enter capacity of this super array: 10
cout << "You entered the following " Enter up to 10 nonnegative numbers.
<< temp.getNumberUsed( ) Place a negative number at the end.
<< " numbers:\n"; 1.1
int index; 2.2
int count = temp.getNumberUsed( ); 3.3
4.4
for (index = 0; index < count; index++)
-1
cout << temp.get(index)<< " ";
You entered the following 4 numbers:
// cout << temp[index] << " ";
1.1 2.2 3.3 4.4
cout << endl; (plus a sentinel value.)
cout << "(plus a sentinel value.)\n"; Test again? (y/n) n
}
10-23
Overloaded [] Operator Definition
for (index = 0; index < count; index++)
cout << temp.get(index)<< " ";
// cout << temp[index] << " ";
double& get( int index);
// double& operator[]( int index);
double& PFArrayD::get( int index){
if (index >= used){
cout << "Illegal index in PFArrayD.\n";
exit(0);
}
return a[index];
}
// double& PFArrayD::operator[]( int index){
// if (index >= used){
// cout << "Illegal index in PFArrayD.\n";
// exit(0);
// }
// return a[index];
// } 10-24
Destructor
• Dynamically-allocated variables do not go away until
"deleted"
• If pointers are only private member data
– They dynamically allocate "real" data in the constructor
– Must have means to "deallocate" when object is destroyed
• Destructor: Opposite of constructor
– Automatically called when object is out-of-scope
– Default version only removes ordinary variables, not dynamic
variables
– Defined like constructor, just add ~
MyClass::~MyClass() PFArrayD::~PFArrayD( ){
{ delete [] a;
//Perform delete clean-up duties }
}
10-25
Copy Constructors
• constructor that has one parameter of the same type as the class
– parameter must be a call-by-reference parameter normally preceded const
PFArrayD( const PFArrayD& obj);
PFArrayD b(20);
for ( int i = 0; i < 20; i++)
b.addElement(i);
PFArrayD temp(b); //Initialized by the copy constructor
PFArrayD::PFArrayD( int size) :capacity(size), used(0){
a = new double[capacity];
}
PFArrayD::PFArrayD( const PFArrayD& obj)
:capacity(obj.getCapacity( )), used(obj.getNumberUsed( )){
a = new double[capacity];
for ( int i = 0; i < used; i++)
a[i] = obj.a[i];
}
10-26
Copy Constructors
• Automatically called when:
1. Class object declared and initialized to other object
2. When function returns class type object
3. When argument of class type is "plugged in“ as actual argument to call-by-
value parameter
• Requires "temporary copy" of object
– Copy constructor creates it
• Default copy constructor
– Like default "=", performs member-wise copy
• Pointers write own copy constructor!
10-27
Shallow and Deep Copies
• Shallow copy
– Assignment copies only member variable contents over
– Default assignment and copy constructors
– Works fine if there are no pointers or dynamically allocated data
involved
• Deep copy
– Pointers, dynamic memory involved
– Must dereference pointer variables to "get to" data for copying
– Write your own assignment overload and copy constructor in this
case!
10-28
For you to think of them
int i = 3;
int* p = i; // invalid conversion from int to int*
int i = 3;
int* p = &i;
int i = 3;
int* p = &i;
int* q = p;
int i = 3;
int* p = &i;
int** q = &p;
For you to think of them
int* p = new int;
*p = 3;
int& q = *p;
q = 4;
cout << *p << endl;
int* r = &q;
*r = 5;
cout << *p << endl;
int*& s = p;
*s = 6;
cout << *p << endl
Copyright © 2017 Pearson Education, Ltd. All rights reserved. 10-30
how a local variable s of type std::string might look when stored in memory
Copyright © 2017 Pearson Education, Ltd. All rights reserved. 10-31