Download presentation
Presentation is loading. Please wait.
Published byJohn Russell Modified over 9 years ago
2
Memory management Arrays Dynamic memory Dynamic arrays and pointers Building a vector class
4
code storage data storage
5
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)
6
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
7
01234567… …10737418161073741817107374181810737418191073741820107374182110737418221073741823 *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)
8
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 variable Space efficiency We don't want to waste memory Low overhead We don't want a large amount of administration
9
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
10
Let's look at a simple example of allocating memory on a stack as described preciously int x = 12; x = x * 3; double d = 23.567; int 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 … …3600360136023603360436053606360736083609361036113612… Why start at byte 3600? No reason, it's just an arbitrary value 12 36 23.567
11
Let's look at a more complex example of allocating memory That includes a main function and two other function calls To 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
12
int main(){ int r = 3; double area = circleArea(r); int 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 circleArea(double radius){ double pi = 3.1415; double sq_r = square(radius); return sq_r * pi; } double square(double x){ return x * x; } double square(double x){ return x * x; } main memory 3 3 3 3 3.1415 start of circleArea's memory square's memory 3 3 r r radius pi x x
13
int main(){ int r = 3; double area = circleArea(r); int 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 circleArea(double radius){ double pi = 3.1415; double sq_r = square(radius); return sq_r * pi; } double square(double x){ return x * x; } double square(double x){ return x * x; } main memory 3 3 3 3 3.1415 start of circleArea's memory 9 9 sq_r r r radius pi
14
int main(){ int r = 3; double area = circleArea(r); int 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 circleArea(double radius){ double pi = 3.1415; double sq_r = square(radius); return sq_r * pi; } double square(double x){ return x * x; } double square(double x){ return x * x; } main memory 3 3 28.274 r r area
15
In stack memory variables for a function are allocated the next bytes in main memory From the last variable allocated space by the same program Once 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
16
Some functions have reference parameters There are two common reasons for doing this Efficiency int sum(const vector & 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 & v) Squares each value in a vector
17
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
18
int main(){ vector vec(3); v[0] = 1; v[1] = 2; v[2] = 3; double vid = 2.397; insert_next(3, vec); int main(){ vector 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 & v){ for(int i=0; i < n; i++) v.push_back(i + v.size() + 1); } void insert_next(int n, vector & v){ for(int i=0; i < n; i++) v.push_back(i + v.size() + 1); } main memory vec vid* 1 1 2 2 3 3 2.397 *very important double *very important double
19
int main(){ vector vec(3); v[0] = 1; v[1] = 2; v[2] = 3; double vid = 2.397; insert_next(vec, 3); int main(){ vector vec(3); v[0] = 1; v[1] = 2; v[2] = 3; double vid = 2.397; insert_next(vec, 3); void insert_next(vector & v, int n){ for(int i=0; i < n; i++) v.push_back(i + v.size() + 1); } void insert_next(vector & v, int n){ for(int i=0; i < n; i++) v.push_back(i + v.size() + 1); } } main memory vec v * vid* 2 2 3 3 *v is a reference to vec, it actually contains the byte address of vec 2.397 *very important double *very important double 3 3 n n o o i i 4 4 1 1 This is a problem, we've just corrupted the first 4 bytes of vid
20
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
21
data storage static code storage automatic dynamic
22
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 them Static storage is used for global constants And other things …
23
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)
24
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!
25
A Digression From Memory
26
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
27
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
28
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 elements int 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
29
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
30
ddouble arr[4]; // array of 4 doubles aarr[0] = 1.23; //assign 1 st element aarr[3] = 2.14; //assign 4 th element ddouble x = 0; xx = arr[3]; //access 4 th element arr[0]arr[1]arr[2]arr[3] the table represents main memory, each cell is an 8 byte double x 1.232.140
31
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
32
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
33
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
34
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 array This is very similar to how we have iterated through the elements of a vector
35
// 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 – 1 The condition is i < ARR_SIZE because the last legal index is ARR_SIZE – 1 Assumes the existence of a constant called ARR_SIZE, e.g. const int ARR_SIZE = 10; Assumes the existence of a constant called ARR_SIZE, e.g. c onst int ARR_SIZE = 10;
36
// 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 A very similar loop to the for loop, we must not forget to increment the index, i
37
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!
38
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 predictable Always check that an index is legal Between 0 and array size – 1
39
Array digression continues …
40
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){
41
// Sums the array, returning the sum int 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 array sz is used to specify the size of the array To use this function give it the appropriate arguments x = sumArrray(myArr, ARR_SIZE); To use this function give it the appropriate arguments x = sumArrray(myArr, ARR_SIZE);
42
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 -1 (an invalid index) if the target isn't found Returns the index of the target as soon as it is found Returns the index of the target as soon as it is found
43
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
44
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 array Consider 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
45
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!
46
What is an array really?
47
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
48
int 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? What has happened? The array passed to the function has changed. This does not happen with other pass-by-value parameters The array passed to the function has changed. This does not happen with other pass-by-value parameters
49
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
50
AAn array is a sequence of bytes in main memory reserved for the array contents ee.g. i nt arr[10]; ▪R▪Reserves 40 contiguous bytes ▪A▪An i nt is 4 bytes, 4 * 10 = 40 ▪E▪Each element can be referenced using indexes TThe variable a rr is just the address of, or a pointer to the first array element CContains the address of the first array element
51
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
52
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
54
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 change We can explicitly declare pointer variables
55
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
56
Main memory is a sequence of bytes int x = 23; Reserves 4 bytes for x in main memory and stores 23 The address of each variable is kept track of in something called a symbol table 02 20 -10 23
57
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
58
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
59
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
60
PPreviously I declared a pointer like this i i nt * p ; ▪T▪The spaces are not necessary YYou can do this i i nt *p ; OOr this i i nt* p ; OOr even this i i nt*p ; But this is kind of ugly!
61
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 *'
62
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 pointer The address operator is the same symbol as the operator to denote reference parameters But they are different operators ▪ Although they perform similar tasks
63
02 12 2 20 -1 23 02 12 2 20 -1 409623 02 12 2 20 -102 12 2 20 -1 23 int x = 23; int* p = &x; p contains the address of x & is the address of operator
64
Pointers can be used to access variables But only after they have been assigned the address of a variable To 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
65
int x = 12; int *p = &x; //assign p the address of x // Use p to assign 23 to x *p = 23; //dereferences p cout << 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
66
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 Cylinder sizeof returns a number of bytes
67
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
68
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)
69
int x = 12; int y = 77; int *p1 = &x; //assign p1 the address of x int *p2 = &y; //assign p2 the address of y p1 p2 12 77 x x y y
70
int x = 12; int y = 77; int *p1 = &x; //assign p1 the address of x int *p2 = &y; //assign p2 the address of y p1 = p2; //assigns the address in p2 to p1 p1 p2 12 77 x x y y
71
12 77 int x = 12; int y = 77; int *p1 = &x; //assign p1 the address of x int *p2 = &y; //assign p2 the address of y *p1 = *p2; p1 p2 77 x x y y
72
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 stack Pointers 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
74
Stack memory is allocated at run time But it has a duration and size that can be easily and correctly predicted Some 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
75
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
76
There 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 …
77
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
78
Data that is allocated dynamically is accessed through pointers When a new dynamic variable is created a pointer is assigned its address Pointers 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
79
And Back To Arrays
80
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
81
Think of memory being used by a program as falling into two categories* Stack memory, the amount to be allocated is easily managed Stack memory, the amount to be allocated is easily managed Dynamic memory, reserved when needed at run-time, but needs more administration to use Dynamic memory, reserved when needed at run-time, but needs more administration to use * In reality it’s a bit more complicated
82
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 The variable is a pointer, used to store an address Creates this array in dynamic memory Creates this array in dynamic memory Data created in dynamic memory persists until the application is terminated, regardless of which function it was created in Data created in dynamic memory persists until the application is terminated, regardless of which function it was created in
83
Once a dynamic array has been created it can be used like any other array Individual array elements are accessed using an index A 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
84
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
85
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
86
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 Deallocates the dynamic memory Indicates that an array is to be deleted Indicates that an array is to be deleted Only memory allocated with new needs to be deleted Only memory allocated with new needs to be deleted
87
An array can be returned from a function But it must be returned as a pointer int* 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 arr should be deleted at some point Use the function something like this: int* arr = oddArray(10); Use the function something like this: int* arr = oddArray(10);
88
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
90
To refer to the same data in multiple ways For example, return a pointer to an element in a data structure To create data in dynamic memory To create dynamic data (strings, vectors,...) To only reserve space for large objects when necessary To create reference structures
91
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
92
Pointers can be used to create variables with no identifiers These variables are accessed using pointers Such 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
93
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
94
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
95
int *p1 = new int; //pointer to an int // Make a new Student, default constructor int *p2 = new Student(); // Make a new Student int *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...
96
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
97
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
98
Stack (non dynamic) memory is released automatically When a function finishes execution all of its memory is released Dynamic memory is only released automatically when an application ends The free store is finite Dynamically allocated memory should be explicitly released when not required
99
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
100
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
101
int* 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; int* 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 array safe since arr is NULL, but pointless results in an error! indicates that arr does not point to anything allocates space for an int array of size sz, and returns its address which is then assigned to arr
102
Bringing It All Toegether …
103
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
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.