1.03k likes | 1.26k Views
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.
E N D
Memory Management • Memory management • Arrays • Dynamic memory • Dynamic arrays and pointers • Building a vector class
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 Consider a computer with 1GB * of RAM *1 GB = 1,073,741,824 bytes 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 variable • Space efficiency • We don't want to waste memory • Low 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 preciously int x = 12; x = x * 3; double d = 23.567; 36 23.567 12 Why start at byte 3600? No reason, it's just an arbitrary value Notice that this example ignores all sorts of complicating issues, functions, reference variables and so on …
Stack and Functions • 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
Another Example double circleArea(double radius){ double pi = 3.1415; double sq_r = square(radius); return sq_r * pi; } int main(){ int r = 3; double area = circleArea(r); double square(double x){ return x * x; } main memory x 3 3 3.1415 3 r radius pi square's memory start of circleArea's memory
Another Example double circleArea(double radius){ double pi = 3.1415; double sq_r = square(radius); return sq_r * pi; } int main(){ int r = 3; double area = circleArea(r); double square(double x){ return x * x; } main memory 3 3 3.1415 9 r radius pi sq_r start of circleArea's memory
Another Example double circleArea(double radius){ double pi = 3.1415; double sq_r = square(radius); return sq_r * pi; } int main(){ int r = 3; double area = circleArea(r); double square(double x){ return x * x; } main memory 3 28.274 r 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 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
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 Size void insert_next(int n, vector <int> & v){ for(inti=0; i < n; i++) v.push_back(i + v.size() + 1); } } int main(){ vector <int> vec(3); v[0] = 1; v[1] = 2; v[2] = 3; double vid = 2.397; insert_next(3, vec); main memory 1 2 3 2.397 vec vid* *very important double
Changing a Vector's Size void insert_next(vector <int> & v, int n){ for(inti=0; i < n; i++) v.push_back(i + v.size() + 1); } } int main(){ vector <int> vec(3); v[0] = 1; v[1] = 2; v[2] = 3; double vid = 2.397; insert_next(vec, 3); This is a problem, we've just corrupted the first 4 bytes of vid main memory 1 2 3 4 2.397 3 o vec vid* v * n i *very important double *v is a reference to vec, it actually contains the byte address of vec
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
Storage code storage data storage static dynamic automatic
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 them • Static 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!
Arrays A 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 collectionof 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 elements name of the array size of the array intmyArray[10] type of the data stored in the array brackets declare that the variable is an 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 • doublearr[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 1.23 2.14 0 2.14 x the table represents main memory, each cell is an 8 byte double
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 • intarr[] = {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 array • This 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 (inti = 0; i < ARR_SIZE; i++){ cout << arr[i] << endl; } 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;
While Loops // Assume an int array named arr, // with a constant called ARR_SIZE // The loop prints the contents of arr inti = 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 predictable • Always check that an index is legal • Between 0 and array size – 1
Arrays And Functions Array 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 • intsumArray(intarr[], intsz){
Sum Function // Sums the array, returning the sum intsumArray(intarr[], intsz){ int sum = 0; for (inti = 0; i < sz; i++){ sum += arr[i]; } return sum; } sz is used to specify the size of the array To use this function give it the appropriate arguments x = sumArrray(myArr, ARR_SIZE);
Searching Arrays • A common array task is to search an array for a particular value int search(intarr[], intsz, int x){ int result = -1; for (inti = 0; i < sz; i++){ if (arr[i] == x) returni; } return result; } Returns the index of the target as soon as it is found Returns -1 (an invalid index) if the target isn't found
Aside – Using Arrays • Make sure that you distinguish between an array and its contents • intarr[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 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
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 Memory What is an array really?
Changing Arrays • What happens if you pass an array to a function that changes the array parameter? voiddoubleArray(intarr[], intsz){ for (inti = 0; i < sz; i++){ arr[i] = arr[i] * 2; } } Notice that this is not pass by reference
Array Parameters intmain() { doublearr[] = {1, 2, 3}; printArray(arr, ARR_SIZE); cout << "Running: void doubleArray"; cout << "(double arr[], intsz);" << 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. intarr[10]; • Reserves 40 contiguous bytes • An int is 4 bytes, 4 * 10 = 40 • Each element can be referenced using indexes • The variable arr is just the address of, or a pointer to the first array element • Contains the address of the first array element