Presentation is loading. Please wait.

Presentation is loading. Please wait.

 Memory management  Arrays  Dynamic memory  Dynamic arrays and pointers  Building a vector class.

Similar presentations


Presentation on theme: " Memory management  Arrays  Dynamic memory  Dynamic arrays and pointers  Building a vector class."— Presentation transcript:

1

2  Memory management  Arrays  Dynamic memory  Dynamic arrays and pointers  Building a vector class

3

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 ddouble arr[4]; // array of 4 doubles aarr[0] = 1.23; //assign 1 st element aarr[3] = 2.14; //assign 4 th element ddouble x = 0; xx = 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 AAn array is a sequence of bytes in main memory reserved for the array contents ee.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 TThe variable a rr is just the address of, or a pointer to the first array element CContains 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

53

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 PPreviously I declared a pointer like this  i i nt * p ; ▪T▪The spaces are not necessary YYou can do this  i i nt *p ; OOr this  i i nt* p ; OOr 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

73

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

89

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


Download ppt " Memory management  Arrays  Dynamic memory  Dynamic arrays and pointers  Building a vector class."

Similar presentations


Ads by Google