Dynamic Data Structures Refer to dynamic memory allocation





































- Slides: 37

Dynamic Data Structures Refer to dynamic memory allocation sections in your text book. E. g. Chapter 13 and Chapter 15 of Bronson.

Dynamic Memory Allocation Static Allocation l l Each variable is assigned storage for a pool of memory The location is fixed for life of variable Arrays created statically l l Size is fixed once E. g. an array of 500 integers l l May only use first few spaces. Rest is wasted OR E. g. an array of 5 integers l l int A[500]; int B[5] What if we need space for 6 integers? 2

Can we allocate space from our pool of memory as required? If we need more memory can we ask for more? If we need less can we free up memory? Can we do this while the program is running? 3

Dynamic memory Allows you to decide at run time how much space is needed. Very useful with lists, stacks and queues. l l l We do not have to worry about specifying an upper limit to the size needed. We can customise our lists to fit the amount of information that will be stored. We can expand shrink the lists to respond to additions and deletions 4

What do you need to do to reserve memory dynamically? You need a special sort of variable that holds an address l A pointer is a variable that holds an address. You must use a special type of declaration to create a pointer variable. l l You need to know what sort of object will be created You need to use a special notation E. g to create a pointer that contains an address of an integer l l int * x; The asterisk informs the compiler that x holds an address. And that this will be an address of an integer. You also need two C++ operators for dynamic memory allocation l new and delete 5

Memory Pool Every program is provided with a pool of memory it can use during execution. This is called the free store or heap The size of this heap depends on your system. Local variables and function parameters are stored in another pool called the stack 6

To create an integer dynamically 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. #include <iostream. h> int main() { int * x; x = new int; *x = cout Create an integer dynamically 8; << “Address of x is “ << &x << endl; << “Address stored in x is “ << x << endl; << “Value stored at address “ << x << “ is “ << *x << endl; delete x; return 0; delete from memory } 7

Typical output: you will have different addresses! Address of x is 0 x 0012 FF 7 C Address stored in x is 0 x 0030 Value stored at address 0 x 0030 is 8 Press any key to continue 8

line 6: Allocation of memory Note x = new int; means grab enough memory for one integer from the heap and return its address and store the address in x line 13: delete x; free up the memory allocated for x for use by this and other programs. 9

Use of &. line 9: means address of Note cout << “Address of x is “ << &x << endl; means display the address of x line 10: cout << “Address stored in x is “ << x << endl; displays what is stored in x. Since x is a pointer it too will be an address Use of *. Line 4: means x is a pointer int * x; Line 11: (*x) Dereferences pointer. “at the address stored in” operator cout << “Value stored at address “ << x << “ is “ << *x << endl; Display the value “at the address stored in” x 10

What use is this? We can grab any amount of memory from the heap To grab enough memory for 100 integers all we need is a pointer to hold one address (of an integer). A request for memory for 100 ints using the new operator. new will grab contiguous space for 100 ints and return the base address to the pointer int *the. Data; the. Data = new int[100]; This could be an expression. E. g. a variable. Suppose we had a data file with an unknown number of records. A varaible n. Items could be used to count the number of records in the file. a block of memory could then be allocated that can contain exaclty the records in the data files. the. Data = new int[n. Items]; 11

Another example int *data; cout << “Enter the number of items to be processed : ”; cin >> n. Items; data = new int[n. Items]; Note the use of square brackets. This looks like an array. Remember an array is a contiguous block of memory. The array name holds the base address of that block of memory. An array name is a special kind of pointer! The point is that once we allocate a block of memory dynamically we can treat the pointer like an array. See Demo 2 12

Dynamic structure Allocation Suppose we have a declaration for a structure struct my. Data. Type { char name[20]; char phone. No[15]; float balance; }; We can create a pointer to a structure. my. Data. Type * my. Ptr; we can create a single object dynamically by my. Ptr = new my. Data. Type; or a dynamically created array by my. Ptr = new my. Data. Type[100]; 13

Accessing parts of a dynamically allocated structure Normally to access fields of a structure we use the dot notation. my. Data. Type obj 1; cin >> obj 1. name; cin >> obj 1. phone. No cin >> obj 1. balance; However if the structure was created dynamically we may be tempted to access fields of the structure using the following syntax my. Data. Type *obj 2; obj 2 = new my. Data. Type; cin >> *obj 2. balance; // WRONG WILL NOT COMPILE! to get it to work we would need to do cin >> (*obj 2). balance; //OK BUT YUCKY 14

SPECIAL SYNTAX FOR Dynamic Structs and Classes Because of the yucky syntax previously the C/C++ language provide a more “natural” syntax to dereference dynamic structs and classes. cin >> obj 2 ->name; cin >> obj 2 ->phone. No; cin >> obj 2 ->balance; 15

Accessing parts of a dynamically allocated array of structures Warning: this is confusing! However if we have an array of structures created dynamically we have to use the normal syntax. my. Data. Type *obj 2; obj 2 = new my. Data. Type[5]; //reserve enough space for 5 my. Data. Type objects //initialise the first two structures cin >> obj 2[0]. name; cin >> obj 2[0]. phone. No; cin >> obj 2[0]. balance; cin >> obj 2[1]. name; cin >> obj 2[1]. phone. No; cin >> obj 2[1]. balance; 16

Passing a dynamically allocated atomic variable to a function #include <iostream. h> void Read. Data(int *data); int main() { int *number; number = new int; Read. Data(number); return 0; } void Read. Data(int *data) { cin >> *data; } //created dynamically //pass by reference - pointer method number contains an address Read. Data needs a parameter that can store an address. data is a pointer so it can receive an address. note to read a value in to the pointed at address we use the dereference 17 operator.

Passing a dynamically allocated array to a function This is exactly the same a passing a statically allocated array to a function Example. #include <iostream. h> void Read. Data(int data[], int n); int main() { int *number; int n. Items; cout << "How many numbers are there to process? : "; cin >> n. Items; number = new int[n. Items]; //created dynamically Read. Data(number, n. Items); //pass and use like a static array return 0; } void Read. Data(int data[], int n) { int i; for (i=0; i<n; i++) cin >> data[i]; } 18

Passing a single structure to a function //Supose we want a function that process a single structure. #include <iostream. h> struct my. Data. Type { char name[20]; int grade; }; void Read. Data(my. Data. Type * rec); DEMO 5 int main() { my. Data. Type *newrecord; newrecord = new my. Data. Type; //created dynamically Read. Data(newrecord); cout << newrecord->name << " " << newrecord->grade << endl; delete record; return 0; } void Read. Data(my. Data. Type *rec) { cout <<" Enter a name : "; cin >> rec->name; cout <<" Enter a grade : "; cin >> rec->grade; } //NOTICE referencing of fields using -> 19

Passing an Array of structs to a function #include <iostream. h> #include <stdlib. h> struct my. Data. Type { char name[20]; int grade; }; DEMO 4 void Read. Data(my. Data. Type data[], int n); int main() { my. Data. Type *record; int n. Items; int i; cout << "How many numbers are there to process? : "; cin >> n. Items; record = new my. Data. Type[n. Items]; //created dynamically Read. Data(record, n. Items); //effectively record used like an array for (i=0; i<n. Items; i++) cout <<record[i]. name << " return 0; " << record[i]. grade << endl; } void Read. Data(my. Data. Type data[], int n) { int i; for (i=0; i<n; i++) { cout << "Enter a name : "; cin >> data[i]. name; cout << "Enter a grade : "; cin >> data[i]. grade; } } 20

Summary: How to use dynamic Arrays Define a pointer variable of the chosen type: The pointer variable will point to the dynamic array in memory and will serve as the name of the dynamic array Call new: Creates the dynamic array. The size of the array is given in the square brackets. Use like an ordinary array Call delete: when you have finished your program before the final return call delete [] to free memory for future use. 21

A Couple of Ideas to think about NULL pointers typedef 22

NULL You often see NULL used in programs. It is a special constant pointer value (a zero address) that can be assigned to any sort of pointer. int *x = NULL; Why would you want to assign NULL to a pointer? l l l Pointers are dangerous, they allow direct access to memory, any memory. It is common practice when creating a pointer to initialise it to NULL. This can be changed e. g. via a new statement. But whenever you delete memory it is good practice to reset a pointer back to NULL again. Also analogous to strings we can make use of the knowledge that a pointer is set to NULL. This is especially true for linked lists. 23

typedef You can define alternative names for existing types l l can aide readability of code is important for some dynamic data structures (linked lists) E. g. typedef int banana; banana x; //variable x is a banana //banana is an int //same as int x; OR More commonly to “clean up pointer declarations typedef float * float. Ptr; float. Ptr f 1 ptr, f 2 ptr; //f 1 prt and f 2 ptr are pointers to floats 24

Practical Application-Linked Lists A dynamic list data structure created using pointers They grow and shrink according to need. Adding items to the list is sometimes called insertion. We have to specify all the operations that we have for array based lists. 25

Nodes We have seen that often useful data items are structured using structs or arrays. We can create special structures that contain pointers to other structures. If the other structure is the same type as itself. We have a self referential data structure. A self referential data structure is often called a NODE Yes this is a bit like explaining cricket to an American! 26

A Node struct Node { int data; Node *next; }; Note reference to a node within node Note use of typedef to create an alias data type pointer to node type. Necessary for function prototypes that use this sort of self referential structure typedef struct Node * Node. Ptr; Node. Ptr front = NULL; Node. Ptr new. Node = NULL; 27

After declaration of pointers front NULL new. Node NULL 28

Insertion to list new. Node = new Node; new. Node->data = 10; new. Node->next = NULL front new. Node NULL data 10 next NULL 29

Insertion to list front = new. Node; front new. Node data 0 next NULL 30

Insertion to list new. Node = new Node; new. Node->data = 20; new. Node->next = NULL front new. Node data 10 next NULL data 20 next NULL 31

front->next = new. Node; new. Node = NULL front new. Node Insertion to list data 10 next NULL data 20 next NULL 32

Node. Ptr cursor; cursor = front; front Deletion from list (note requires another pointer) data 10 next NULL cursor new. Node data 20 next NULL 33

front = front->next; cursor front new. Node //deleting first node data 10 next NULL data 20 next NULL 34

delete cursor; cursor = NULL; temp front new. Node NULL data 20 next NULL 35

Demo 6 and 7 36

Linked lists They require very precise programming and a high level of understanding of manipulation of data by addresses. Writing functions that manipulate linked lists also requires a very clear understanding of pointers. More next week. 37