Upload
dasha
View
87
Download
1
Tags:
Embed Size (px)
DESCRIPTION
Pointers and Dynamic Memory. Memory Management. Memory management Arrays Dynamic memory Dynamic arrays and pointers Building a vector class. Memory Management. Memory Requirements. code storage. data storage. Memory Management. - PowerPoint PPT Presentation
Citation preview
Pointers and Dynamic Memory
Memory ManagementMemory managementArraysDynamic memoryDynamic arrays and pointersBuilding a vector class
Memory Management
Memory Requirements
code storage
data storage
Memory Management
When a program runs memory has to be allocated for its processes and its variables That is, working memory, or RAM
There needs to be a system for efficiently allocating such memory We will just consider how memory is
allocated to program data (variables)
RAM
We can consider Random Access Memory (RAM) as a long sequence of bytes Starting with 0 Ending with the amount of main memory
(-1)RAM is addressable and supports
random access That is, we can go to byte 2,335,712
without having to visit all the preceding bytes
RAM Illustrated
0 1 2 3 4 5 6 7 …
… 1073741816
1073741817
1073741818
1073741819
1073741820
1073741821
1073741822
1073741823
*1 GB = 1,073,741,824 bytes
Consider a computer with 1GB * of RAM
This is a fairly abstract illustration (so ignores a variety of issues)
Goals
As much as possible we would like to satisfy three goals when allocating memory
Time efficiency That is, we should be able to quickly find
sufficient memory to store a variableSpace efficiency
We don't want to waste memoryLow overhead
We don't want a large amount of administration
Stack Memory
There is a very simple way of organizing memory that meets these goals Assign memory locations to variables in
sequence▪ With no gaps
Once a variable is no longer required release the memory, allowing it to be over-written
We will call this organization the stack Or automatic memory Although the stack cannot be used for
everything
Stack Memory – Simple Example
Let's look at a simple example of allocating memory on a stack as described preciouslyint x = 12;x = x * 3;double d = 23.567;
Notice that this example ignores all sorts of complicating issues, functions, reference variables and so on …
… 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 …
Why start at byte 3600?
No reason, it's just an arbitrary value
1236 23.567
Stack and Functions
Let's look at a more complex example of allocating memory That includes a main function and two
other function callsTo make the example a bit simpler I
will stop showing actual byte values And just use coloured boxes to represent
the memory allocated to variables
Another Exampleint main(){ int r = 3; double area = circleArea(r);
double circleArea(double radius){ double pi = 3.1415; double sq_r = square(radius); return sq_r * pi;}
double square(double x){ return x * x;}main memory
3 3 3.1415
start of circleArea's memorysquare's memory
3r radius pi
x
Another Exampleint main(){ int r = 3; double area = circleArea(r);
double circleArea(double radius){ double pi = 3.1415; double sq_r = square(radius); return sq_r * pi;}
double square(double x){ return x * x;}main memory
3 3 3.1415
start of circleArea's memory
9sq_rr radius pi
Another Exampleint main(){ int r = 3; double area = circleArea(r);
double circleArea(double radius){ double pi = 3.1415; double sq_r = square(radius); return sq_r * pi;}
double square(double x){ return x * x;}main memory
3 28.274r area
Stack Memory
In stack memory variables for a function are allocated the next bytes in main memory From the last variable allocated space by
the same programOnce a function ends its memory is
released Remember that a function's variables
only exist while the function is executing So the memory previously allocated to a
completed function can be re-used
Reference Parameters
Some functions have reference parameters
There are two common reasons for doing this
Efficiency int sum(const vector<int> & v) Sums the contents of the vector
And because we want the changes made to the variable to persist after the function void square_all(vector <int> & v) Squares each value in a vector
Stack Problems
There is one big issue with stack memory Because memory is allocated in
sequence it is not possible to change the byte size of a variable
Strings and vectors frequently change size It is more correct to say that a string
variable may refer to strings of different sizes
A vector can change size by using its push_back method
Changing a Vector's Sizeint main(){ vector <int> vec(3); v[0] = 1; v[1] = 2; v[2] = 3; double vid = 2.397; insert_next(3, vec);
void insert_next(int n, vector <int> & v){ for(int i=0; i < n; i++) v.push_back(i + v.size() + 1); }}
main memory
vec vid*1 2 3 2.397
*very important double
Changing a Vector's Sizeint main(){ vector <int> vec(3); v[0] = 1; v[1] = 2; v[2] = 3; double vid = 2.397; insert_next(vec, 3);
void insert_next(vector <int> & v, int n){ for(int i=0; i < n; i++) v.push_back(i + v.size() + 1); }}
main memory
vec v *vid*2 3
*v is a reference to vec, it actually contains the byte
address of vec
2.397
*very important double
3n
oi
41
This is a problem, we've just corrupted the first 4 bytes of vid
Types Of Memory
It turns out you can divide memory into three broad categories Static Automatic Dynamic
These types of memory are typically used for different sorts of program data Although C++ gives programmers
choice over which type of memory to use
data storagestatic
Storage
code storage
automatic
dynamic
Static Storage
Statically stored variables last for the lifetime of a program
The number of static variables does not change as a program runs So no special system is required to
maintain themStatic storage is used for global
constants And other things …
Automatic Storage
Function variables and parameters use automatic storage by default
Automatic variables are typically managed on a stack as we have been discussing The stack is an allocated area of
memory that grows and shrinks as functions are called
Memory is allocated sequentially (without gaps)
Functions and Memory
A variable defined in a function block only persists for the lifetime of that function call Unless it is declared as static
Consider what memory might be allocated when a function is running Memory required for the function’s data
and only required during the function call
Memory that is to persist beyond the lifetime of the function – we can't use the stack for this!
ArraysA Digression From Memory
Arrays
Before looking at dynamic memory let's look at a basic data structure – the array Arrays are used to store multiple values
of the same type, much as vectors do Except that arrays are much more basic
data structures without methods (such as size)
Arrays are indexed and we can either refer to individual elements or the entire array Much like vectors
What Is An Array?An array variable is a collection of
other variables You can think of an array as something
that contains variables This is important because an integer
array is not an integer, it is a collection of integers
The items stored in an array (elements) are stored sequentially in main memory Just like variables on the stack
Declaring Arrays
An array is declared with a type, and []s to indicate that the variable is an array The type declares the type of the array
elementsint myArray[10]
type of the data stored in
the array
brackets declare that the variable is an array
size of the array
name of the array
Array Indexing
The elements of the array are accessed using an index The indexes are addresses of the
elements▪ The first element always has an index of 0▪ The last index is always array size – 1
Array indexes follow the name of the array and are enclosed in []s Individual array elements are used in
exactly the same way as variables
Using an Array double arr[4]; // array of 4 doubles arr[0] = 1.23; //assign 1st element arr[3] = 2.14; //assign 4th element double x = 0; x = arr[3]; //access 4th element
arr[0] arr[1] arr[2] arr[3]
the table represents main memory, each cell is an 8 byte double
x1.23 2.14 02.1
4
Initializing Arrays
Array elements have to be given values individually This is often done in a loop
There is a special shorthand notation for initializing arrays when they are declared int arr[] = {1, 3, 7, 9}; This shorthand notation is only allowed
on the same line as the declaration
Array Size
An array's size must be specified when it is declared It must be a literal (i.e. a number) or A constant It can't be given using a variable
An array's size cannot change during the life of a program Arrays can therefore be allocated space
in automatic memory as their size cannot change
Arrays and Vectors
Arrays are not vectors, and are not classes Arrays do not have methods▪ Like size or push_back
The size of an array has to be recorded in a separate variable, and may not be changed
An array is essentially a way of referring to a collection of values of the same type Its elements
Arrays and Loops
Arrays are often processed using loops Allowing each element to be accessed in
turn The loop control variable is used as an
index into the array The loop ends once the array has been
processed▪ Often when the loop control variable is equal
to the size of the arrayThis is very similar to how we have
iterated through the elements of a vector
For Loops
// Assume an int array named arr,// with a constant called ARR_SIZE// The loop prints the contents of arr
for (int i = 0; i < ARR_SIZE; i++){cout << arr[i] << endl;
}The condition is i < ARR_SIZE because the last legal index is ARR_SIZE – 1Assumes the existence of a constant called ARR_SIZE, e.g. const int ARR_SIZE = 10;
While Loops
// Assume an int array named arr,// with a constant called ARR_SIZE// The loop prints the contents of arr
int i = 0;while (i < ARR_SIZE){
cout << arr[i] << endl;i++;
}
A very similar loop to the for loop, we must not forget to increment the index, i
Size as a Constant
It is much better to define array size as a constant than to use a literal value (like 10)
If the programmer wants to change the array size this only needs to be done once Use the constant whenever the array
size is referenced, and Avoid using magic numbers!
Index Out of Bounds
What happens if a program attempts to access a non-legal index?
A run-time error occurs, either An illegal attempt to access a memory
location has been made (… stack is corrupted …) , or
Something less predictableAlways check that an index is legal
Between 0 and array size – 1
Arrays And FunctionsArray digression continues …
Array and Functions
Arrays can be passed to functions as parameters Parameter lists can include arrays The array type, and the fact that it is an
array must be specified in the parameter list▪ An array is specified just like any declaration
Here is the header for an array sum function int sumArray(int arr[], int sz){
Sum Function
// Sums the array, returning the sumint sumArray(int arr[], int sz){
int sum = 0;for (int i = 0; i < sz; i++){
sum += arr[i];}return sum;
} sz is used to specify the size of the arrayTo use this function give it the appropriate argumentsx = sumArrray(myArr, ARR_SIZE);
Searching Arrays
A common array task is to search an array for a particular value
int search(int arr[], int sz, int x){int result = -1;for (int i = 0; i < sz; i++){
if (arr[i] == x)return i;
}return result;
} Returns -1 (an invalid index) if the target isn't found
Returns the index of the target as soon as it is found
Aside – Using Arrays
Make sure that you distinguish between an array and its contents int arr[4];▪ arr is the entire array▪ arr[1] is one element of the array
The array is a sequence of integers, one element is an integer They are not interchangeable
Is the Array Full?
It is often important to know how much of an array is used Unassigned elements shouldn't be
processed▪ e.g. summing or calculating the average of an
arrayConsider what input a function
requires Array, and its maximum size, or Array, and current size ,or▪ i.e. the number of elements actually used
Array, maximum size, and current size
Returning Arrays
Arrays can be returned from a function However the obvious way of specifying
the return type is illegal▪ int[] getAnArray() { …
In addition, returning an array raises the question of how big the array is
Before continuing this we need to know more about how arrays really work
This does not work!
Arrays And MemoryWhat is an array really?
Changing Arrays
What happens if you pass an array to a function that changes the array parameter?
void doubleArray(int arr[], int sz){for (int i = 0; i < sz; i++){
arr[i] = arr[i] * 2;}
}
Notice that this is not pass by reference
Array Parametersint main(){
double arr[] = {1, 2, 3};printArray(arr, ARR_SIZE);cout << "Running: void doubleArray";cout << "(double arr[], int sz);" << endl;doubleArray(arr, ARR_SIZE);printArray(arr, ARR_SIZE);return 0;
}
What has happened? The array passed to the function has changed. This does not happen with other pass-by-value parameters
What's an Array?
To understand what is going on in the previous example we need to know more It's easy to think of an array as a
container, like a bookcase or a filing cabinet
But these are structures in their own right
An array is just a collection of values All of the same type, and Stored in sequence in main memory It is not an object of a class like a vector
More About Arrays
An array is a sequence of bytes in main memory reserved for the array contents e.g. int arr[10]; ▪ Reserves 40 contiguous bytes▪ An int is 4 bytes, 4 * 10 = 40▪ Each element can be referenced using
indexesThe variable arr is just the address
of, or a pointer to the first array element Contains the address of the first array
element
More About Indexing
Consider this assignment statement: arr[8] = 23;
To find this array element Look up the address stored in arr Multiply type size (4 for an int) by the
index Add this to the address in arr to find the
address of arr[8] element Known as an offset calculation
Arrays and Functions
When an array is passed to a function it is passed much like a reference parameter That is, the function is given the main
memory location of the first item in the array
But this is because array variables are addresses, not because they are inherently pass-by-reference
Because the address is passed any changes to the array are reflected outside the function If this is to be avoided make a copy in
the function
Pointer Basics
Arrays are Pointers
So an array variable is really just the address of the first element of the array Note that arr in int arr[10] is not of type
int It is a type that called a pointer▪ Actually a pointer to an int
Array variables are special kinds of pointer in that they are constant▪ The address stored in the pointer cannot
changeWe can explicitly declare pointer
variables
Variables Review
A variable is a location in main memory where data is stored The type of a variable indicates the
amount of main memory required to store the data, and
The operations that may be performed upon the data
Every variable has a byte address Its location in main memory Which is stored by the system in some
way
Main Memory
Main memory is a sequence of bytes
int x = 23;
Reserves 4 bytes for x in main memory and stores 23The address of each variable is kept
track of in something called a symbol table
0 220-1
0 220-1
23
Pointers
A pointer is a special type of variable That stores an address rather than a
value They are called pointers as they can be
considered to point to a variable It is necessary to record the type of
data that a pointer variable points to So that the appropriate operations can
be performed on the value it points to
Pointers and Types
Pointers store addresses Addresses are always the same size on
the same system▪ Often 8 bytes
So why do we have to say what type of data is going to be pointed to? To reserve enough space for the data
and To ensure that the appropriate
operations are used with the data
Declaring a Pointer
Pointer variable are identified by an * that follows the type in the declaration int * p;
This declares a variable called p that will point to (or refer to) an integer
Note that the type of a pointer is not the same as the type it points to p is a pointer to an int, and not an int
Declaring a Pointer
Previously I declared a pointer like this int * p;▪ The spaces are not necessary
You can do this int *p;
Or this int* p;
Or even this int*p;
But this is kind of ugly!
Pointers and Values
The operation shown below is illegal int x = 12; int *p = x;
Remember that the type of p is an address (to an int), and not an int Addresses are actually whole numbers
but it is illegal to assign arbitrary numbers to them
This is a good thing!
error C2440: 'initializing' : cannot convert from 'int' to
'int *'
Address Operator
Pointers can be assigned the address of an existing variable Using the address operator, & In this way it is possible to access the
variable using a pointerThe address operator is the same
symbol as the operator to denote reference parameters But they are different operators▪ Although they perform similar tasks
0 212 220-1
23
0 212 220-1
4096
23
0 212 220-1
0 212 220-1
23
Main Memory and Pointers
int x = 23;int* p = &x;
p contains the address of x& is the address of operator
Dereferencing
Pointers can be used to access variables But only after they have been assigned
the address of a variableTo change the value of a variable a
pointer points to the pointer has to be dereferenced Using the * operator which can be
thought of meaning the variable pointed to
Pointer Assignment
int x = 12;int *p = &x; //assign p the address of x// Use p to assign 23 to x*p = 23; //dereferences pcout << x << endl;cout << p << endl;cout << *p << endl; value of
x
value of the variable that p points to (i.e. x)
address of x
The sizeof Operator
The sizeof operator allows us to find out the size of a type or variable cout << sizeof(int); cout << sizeof(‘c’); //size of a char cout << sizeof(Cylinder); //the size of a
Cylindersizeof returns a number of bytes
Reference Operator
The & operator is also used to create reference parameters Where a variable is passed to a function
by reference, rather than by value Reference parameters are not pointers
A reference parameter is given the address of the argument passed to the function And thereafter behaves like a normal
variable
Overloaded Operators
C++ tends to re-use operators The meaning can be determined by the context
The * operator multiplication: 12 * 3; declaration of a pointer: int* p; dereference: *p = 23;
The & operator address of: p = &x; pass by reference: void swap(int & x, int & y)
Pointers and Assignmentint x = 12;int y = 77;int *p1 = &x; //assign p1 the address of xint *p2 = &y; //assign p2 the address of y
p1p2
1277
xy
Pointers and Assignmentint x = 12;int y = 77;int *p1 = &x; //assign p1 the address of xint *p2 = &y; //assign p2 the address of yp1 = p2; //assigns the address in p2 to p1
p1p2
1277
xy
1277
Pointers and Assignmentint x = 12;int y = 77;int *p1 = &x; //assign p1 the address of xint *p2 = &y; //assign p2 the address of y*p1 = *p2;
p1p2 7
7
xy
Why Use Pointers?
In practice we don't often use pointers like the preceding examples There is little point in making pointers to
individual integers stored on the stackPointers are key to managing
memory for objects that change size during run-time Such objects are allocated space in
another area of main memory – in dynamic memory
Dynamic Memory
Stack and Dynamic Memory
Stack memory is allocated at run time But it has a duration and size that can
be easily and correctly predictedSome data requires memory of
unknown size or duration That is, as programmers we don't know
how much memory the program will require
Such data should be stored in dynamic memory
Dynamic Memory
Dynamic memory allows memory usage to be determined at run-time
Allows users to decide the size of data structures like vectors and dynamic arrays
Allows space to be reserved for large objects only as it is needed
Uses of Dynamic MemoryThere are many examples of data
that is stored dynamically Vectors and dynamic arrays Reference structures such as linked lists Variable size character strings Object variables▪ Although these do not have to be stored
dynamically …
Why Not Use the Stack
Data that is stored dynamically may Change size, and may Persist beyond the duration of a function
It cannot therefore be stored on the stack Because stack memory is allocated
sequentially And all memory associated with a
function is released when the function ends
Pointers
Data that is allocated dynamically is accessed through pointers When a new dynamic variable is created
a pointer is assigned its addressPointers point to data in dynamic
memory Pointers themselves are usually
automatic variables and therefore reside on the stack
One common use of pointers is in creating dynamic arrays
Dynamic ArraysAnd Back To Arrays
Array Size Revisited
As noted previously array size must be given a constant value And the size of an array cannot be
changed while the program is running (during run-time)
Which supports the organization of stack memory
It is possible to allocate memory at run-time From a free store of memory, called
dynamic memory
Dynamic Memory
Think of memory being used by a program as falling into two categories*Stack memory, the
amount to be allocated is easily managed
Dynamic memory, reserved when needed at run-time, but needs more administration to use
* In reality it’s a bit more complicated
Dynamic Arrays
int arrSize = 0;cout << "How big an array? ";cin >> arrSize;int* arr = new int[arrSize];
The variable is a pointer, used to store an address
Creates this array in dynamic
memoryData created in dynamic memory persists until the application is terminated, regardless of which function it was created in
Using Dynamic Arrays
Once a dynamic array has been created it can be used like any other array Individual array elements are accessed
using an indexA dynamic array can be passed to a
function that takes an array parameter The only difference is that a dynamic
array's address can be changed Allowing it to point to a new array
Deleting Arrays
Dynamic memory continues to be allocated even outside the scope in which it was created Whereas memory for a function's
parameters and variables is released when the function ends
Dynamic memory remains allocated until The application terminates, or It is explicitly released
delete
Every dynamic array created by a program must also be deleted This also applies any dynamic memory
as we will see later Use delete[] to delete a dynamic array
A simple rule is that Any variable that is created using the
keyword new must be deleted by the programmer
Dynamic Arrays
int arrSize = 0;cout << "How big an array? ";cin >> arrSize;int* arr = new int[arrSize];// … arr is used in the program …delete[] arr;
Deallocates the dynamic memory
Indicates that an array is to be deleted
Only memory allocated with new needs to be deleted
Returning Arrays
An array can be returned from a function But it must be returned as a pointerint* oddArray(int sz){
int* result = new int[sz];for (int i = 0; i < sz; i++){
result[i] = i * 2 + 1;}return result;
}
arr should be deleted at some point
Use the function something like this:int* arr = oddArray(10);
Returning Array Size
In the previous example an array was created in a function and then returned The size of the array was known outside
the function, and passed to the function If a function creates an array of
unknown size, the size must also be returned This may require the use of a reference
(&) parameter to access the size
Pointers and Objects
Use Pointers ...
To refer to the same data in multiple ways For example, return a pointer to an
element in a data structureTo create data in dynamic memory
To create dynamic data (strings, vectors, ...)
To only reserve space for large objects when necessary
To create reference structures
Memory
Memory used by a program can be broken down into two main components
Stack memory, used to store variables in the main and other functions Stores any variable not created with new
Dynamic memory Often referred to as the free store or the
heap Stores data created with new
New
Pointers can be used to create variables with no identifiers These variables are accessed using
pointersSuch variables are dynamically
allocated They are created and destroyed as the
program is running This allows, for example, vectors to be
created with the size determined at run-time▪ This is hidden from the user of the vector
The NULL Pointer
Pointers store memory addresses Initially a pointer may not refer to a
memory address So does not have a meaningful value
The constant, NULL is used to indicate that a pointer's address has not been assigned NULL actually equals 0 As memory addresses are in fact whole
numbers
Pointers and Classes
Pointers are frequently used with classes
Some objects can be very large Objects may contain bitmaps, or sound
files Without dynamic memory space has to
be reserved for objects that may not be used
Pointers must be dereferenced to access an object's methods
New Example
int *p1 = new int; //pointer to an int// Make a new Student, default constructorint *p2 = new Student(); // Make a new Studentint *p3 = new Student(94101); *p1 = 119;int id = p2.getID();
Error! p2 is not a Student it is an address ...
... assumes the existence of a Student class ...
Pointers and Objects
A pointer must be dereferenced to access the methods or attributes of an object That the pointer points to
This has to be done carefully, for example: p2.displayStudent(); //error! *p2.displayStudent (); //error! (*p2).displayStudent (); //works
It is much easier to use the -> operator
Referring to Objects
The -> operator can be used to refer to an object's methods using a pointer Student* p = new Student(94101); p->addGrade(3, 3.33);
The -> operator is only used with pointers It should not be used with regular object
variables Use dot notation for such variables
Deletion
Stack (non dynamic) memory is released automatically When a function finishes execution all of
its memory is releasedDynamic memory is only released
automatically when an application ends The free store is finite Dynamically allocated memory should
be explicitly released when not required
Deleting Memory
The keyword delete is used to free dynamically allocated memory
When deleting a dynamic array delete must be followed by []s
Memory space that has been deallocated cannot be freed again Attempting to do this results in an error
delete Operator
The delete operator is used to free memory that has been dynamically allocated int * p = new int; *p = 23; ... delete p;
Each new should have a matching delete Although often not in the same function
New and Deleteint* arr = NULL;int sz = 0;cout << “Please enter size”;cin >> sz;arr = new int[sz];int* p_int = new int(12);delete p_int;delete[] arr;arr = NULL;delete[] arr;delete p_int;
frees up 4 bytes
frees up the 40 bytes allocated for the dynamic
arraysafe since arr is NULL, but
pointlessresults in an error!
indicates that arr does not point to
anythingallocates space for an int
array of size sz, and returns its address which is
then assigned to arr
Building a Simple Vector ClassBringing It All Toegether …
Building a Vector Class
We will write a class that uses pointers and dynamic arrays to behave much like a vector
To keep things simple we will just Implement a vector of doubles rather
than a template for any type Implement push_back and size methods Start by allowing access to the vector
elements using get(index) and set(index)The example will be developed in
class