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
Each variable is assigned storage for a pool of
memory
The location is fixed for life of variable
Arrays created statically
Size is fixed once
E.g. an array of 500 integers
int A[500];
May only use first few spaces. Rest is wasted
OR
E.g. an array of 5 integers
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.
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 and 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
A pointer is a variable that holds an address.
You must use a special type of declaration to create a pointer
variable.
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
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
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. #include <iostream.h>
2.
3. int main() {
4. int * x;
5. Create an integer
6. x = new int; dynamically
7.
8. *x = 8;
9. cout << “Address of x is “ << &x << endl;
10. cout << “Address stored in x is “ << x << endl;
11. cout << “Value stored at address “ << x << “ is “ << *x << endl;
12.
13. delete x;
14. return 0; delete from memory
15. }
7
Typical output: you will have different addresses!
Address of x is 0x0012FF7C
Address stored in x is 0x00300030
Value stored at address 0x00300030 is 8
Press any key to continue
8
Note
line 6: Allocation of memory
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 &.
Note
line 9: means address of
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 *theData;
theData = 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 nItems 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.
theData = new int[nItems]; 11
Another example
int *data;
cout << “Enter the number of items to be processed : ”;
cin >> nItems;
data = new int[nItems];
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 myDataType {
char name[20];
char phoneNo[15];
float balance;
};
We can create a pointer to a structure.
myDataType * myPtr;
we can create a single object dynamically by
myPtr = new myDataType;
or a dynamically created array by
myPtr = new myDataType[100];
13
Accessing parts of a dynamically allocated structure
Normally to access fields of a structure we use the dot notation.
myDataType obj1;
cin >> obj1.name;
cin >> obj1.phoneNo
cin >> obj1.balance;
However if the structure was created dynamically we may be tempted to
access fields of the structure using the following syntax
myDataType *obj2;
obj2 = new myDataType;
cin >> *obj2.balance; // WRONG WILL NOT COMPILE!
to get it to work we would need to do
cin >> (*obj2).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 >> obj2->name;
cin >> obj2->phoneNo;
cin >> obj2->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.
myDataType *obj2;
obj2 = new myDataType[5]; //reserve enough space for 5 myDataType objects
//initialise the first two structures
cin >> obj2[0].name;
cin >> obj2[0].phoneNo;
cin >> obj2[0].balance;
cin >> obj2[1].name;
cin >> obj2[1].phoneNo;
cin >> obj2[1].balance;
16
Passing a dynamically allocated atomic variable to a function
#include <iostream.h>
void ReadData(int *data);
int main() {
int *number;
number = new int; //created dynamically
ReadData(number); //pass by reference - pointer method
return 0;
} number contains an address
ReadData needs a parameter that
void ReadData(int *data) {
cin >> *data;
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
operator. 17
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 ReadData(int data[], int n);
int main() {
int *number;
int nItems;
cout << "How many numbers are there to process? : ";
cin >> nItems;
number = new int[nItems]; //created dynamically
ReadData(number, nItems); //pass and use like a static array
return 0;
}
void ReadData(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 myDataType {
char name[20];
int grade;
};
void ReadData(myDataType * rec);
int main() {
myDataType *newrecord;
DEMO5
newrecord = new myDataType; //created dynamically
ReadData(newrecord);
cout << newrecord->name << " " << newrecord->grade << endl;
delete record;
return 0;
}
void ReadData(myDataType *rec) {
cout <<" Enter a name : "; //NOTICE referencing of fields using ->
cin >> rec->name;
cout <<" Enter a grade : ";
cin >> rec->grade;
}
19
Passing an Array of structs to a function
#include <iostream.h>
#include <stdlib.h>
struct myDataType {
char name[20];
int grade;
};
void ReadData(myDataType data[], int n); DEMO 4
int main() {
myDataType *record;
int nItems;
int i;
cout << "How many numbers are there to process? : ";
cin >> nItems;
record = new myDataType[nItems]; //created dynamically
ReadData(record, nItems); //effectively record used like an array
for (i=0;i<nItems;i++)
cout <<record[i].name << " " << record[i].grade << endl;
return 0;
}
void ReadData(myDataType 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?
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
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 * floatPtr;
floatPtr f1ptr,f2ptr; //f1prt and f2ptr 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 { Note reference to a
node within node
int data;
Node *next; 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 * NodePtr;
NodePtr front = NULL;
NodePtr newNode = NULL;
27
After declaration of pointers
front NULL
newNode NULL
28
Insertion to list
newNode = new Node;
newNode->data = 10;
newNode->next = NULL
front NULL
data 10
newNode
next NULL NULL
29
Insertion to list
front = newNode;
front
data 0
newNode
next NULL NULL
30
Insertion to list
newNode = new Node;
newNode->data = 20;
newNode->next = NULL
data 10
front
next NULL NULL
data 20
newNode
next NULL NULL
31
front->next = newNode; Insertion to list
newNode = NULL
data 10
front
next NULL
data 20
next NULL NULL
newNode NULL
32
NodePtr cursor; Deletion from list (note
cursor = front; requires another pointer)
data 10
front
next NULL
cursor
data 20
next NULL NULL
newNode NULL
33
front = front->next; //deleting first node
data 10
cursor
next NULL
data 20
front
next NULL NULL
newNode NULL
34
delete cursor;
cursor = NULL;
temp
NULL
data 20
front
next NULL NULL
newNode 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