Download Topic12

Document related concepts

Array data structure wikipedia , lookup

B-tree wikipedia , lookup

Linked list wikipedia , lookup

Transcript
Topic 12 – Dynamic Memory
Allocation & Linked Lists
Static Memory Allocation



So far, we have only seen static memory
allocation.
This means that the number of variables a
program will use, and their sizes, are fixed
and known when the program is written and
compiled.
It is possible to read through a program and
calculate exactly how much memory will be
required when the program is run, and how
much memory each variable and/or data
structure will need.
CISC105 – Topic 12
Static Memory Allocation



Notice that this requires that the maximum
number of elements in an array must be
known when the program is written.
This is not practical, as the total number of
variables may not be known in advance.
For example, in writing a student record
database program, the number of students
may not be known in advance. This may
result in far too much memory being
allocated or not enough.
CISC105 – Topic 12
Static Memory Allocation




It is clear why the approach we have seen
thus far is limited.
We need to know how many variables the
program will need in advance.
If this number is too low, the program will not
be able to handle the necessary data.
If this number is too high, the program will
waste memory, as all of the allocated memory
will not be used.
CISC105 – Topic 12
Dynamic Memory Allocation



These disadvantages are overcome by
making use of dynamic memory allocation.
This refers to a program deciding at run-time
how much memory to allocate.
A program can call certain functions to
allocate memory at any time. Therefore, the
program can use input data as the basis for
determining how much space to request and
what type of data to store in this space.
CISC105 – Topic 12
Dynamic Memory Allocation


As dynamic memory allocation allows
the program to make direct use of
memory, it is clear that pointers must
be used in some fashion.
Using dynamic data structures requires
the sophisticated use of pointers; thus
we will briefly summarize the use of
pointers we have covered thus far.
CISC105 – Topic 12
A Quick Review of Pointers



A pointer is a variable that holds a memory
address.
We can use the “*” operator to dereference a
pointer. This refers to “following the pointer”
to get what it is pointing to.
We can pass pointers to (the addresses of)
variables into functions; this allows the
function to modify variables local to the
calling function (call-by-reference). This
allows the use of function output parameters.
CISC105 – Topic 12
A Quick Review of Pointers



The name of an array is simply the address of
the first element of that array. Thus, we can
think of an array name as a pointer to the
array.
A string is simply an array of characters, thus
it behaves just like an array.
As arrays are simply pointers to their first
elements, anytime we pass an array (or
string) into a function, this call is call-byreference. The function can modify the array.
CISC105 – Topic 12
Something New…Kind of.
Null Pointers




A pointer is a memory address.
Any pointer can have a special value, NULL. This
value means “does not point to anything.”
If a pointer is set to NULL, it cannot be
dereferenced. Any attempt to do so will cause a
segmentation fault.
A pointer is set to NULL using the standard
assignment statement syntax:
int *num1_p;
num1_p = NULL;
CISC105 – Topic 12
Null Pointers

We can also test to see if a pointer is set to
NULL, using standard comparisons.
if (num1_p == NULL)
{ /* points to nothing */ }
else
{ /* points to something */ }

Why this is an important concept will become
clear later. Null pointers are used extensively
to mark the end of dynamic data structures,
such as linked lists.
CISC105 – Topic 12
A Quick Review of Pointers


When we define structures, we can also
create pointers to variables of these
user-defined datatypes.
Thus, we can see that pointers are
simply memory addresses. They are
the components of the C language used
to manage memory.
CISC105 – Topic 12
The malloc Function




We now introduce the malloc() function.
This function name is short for memory
allocation.
When this function is called, the program
finds some unallocated space in memory.
This function takes exactly one parameter,
the amount of memory that the program
wishes to allocate.
It returns a pointer, of type void * which is
the address where the memory that was just
allocated begins.
CISC105 – Topic 12
The malloc Function


The one parameter passed into malloc is
the size of the memory block we wish to
allocate.
This size is determined using the sizeof()
function. This function takes a datatype
as a parameter and returns the size of the
datatype (how much space in memory one
variable of that datatype occupies in
memory).
CISC105 – Topic 12
The malloc Function


Thus, to allocate enough space in memory to
store an integer, we would call malloc as:
malloc(sizeof(int));
Similarly, to allocate enough space in memory
to store a student_record, we would call
malloc as:
malloc(sizeof(student_record));
CISC105 – Topic 12
The malloc Function




So, now we can allocate memory of the correct
size to store a variable…how do we use it?
Remember that pointers can only point to a
certain datatype, i.e. an int * can only point to
an integer, nothing else.
malloc returns a void *. This is simply a
memory address with no type.
Therefore, we must cast the return value of
malloc, the address of the just-allocated
memory block, to the type of pointer we want to
store in that memory block.
CISC105 – Topic 12
The malloc Function

Thus, to allocate enough space in memory to
store an integer, we would call malloc as:
int *int1_p;
int1_p = (int *)malloc(sizeof(int));

Similarly, to allocate enough space in memory
to store a student_record, we would call
malloc as:
student_record *TopStudent_p;
TopStudent_p =
(student_record *)malloc(sizeof(student_record));
CISC105 – Topic 12
The malloc Function
student_record *TopStudent_p;
TopStudent_p =
(student_record *)malloc(sizeof(student_record));

What this call to malloc does is to allocate
memory the size of one student_record.
The memory address of this block of memory
is stored in the pointer TopStudent. We
cast this address to a student_record
pointer; this indicates that we will use this
block of memory as a student_record
datatype.
CISC105 – Topic 12
Referencing
Dynamically Allocated Memory


So, now we can allocate memory
dynamically and obtain a pointer to a
specific datatype, which will be stored
in the newly allocated memory.
Once this is done, values can be stored
in the newly allocated memory using
the pointer referencing operator “*”,
just as pointers were used before.
CISC105 – Topic 12
Referencing
Dynamically Allocated Memory
int *num1_p, *num2_p;
student_record *record1_p, *record2_p;
num1_p = (int *)malloc(sizeof(int));
num2_p = (int *)malloc(sizeof(int));
record1_p = (student_record *)
malloc(sizeof(student_record));
record2_p = (student_record *)
malloc(sizeof(student_record));
*num1_p = 27; *num2_p = 99;
(*record1_p).letter_grade = ‘A’;
strcpy(record2_p->last,”Hall”);
strcpy(record2_p->first,”Michael”);
CISC105 – Topic 12
The calloc Function


The malloc function works well to
allocate one variable, as we simply pass
it the size of memory we need to
allocate, which is easily obtained by
using the sizeof function.
Sometimes we wish to allocate arrays
dynamically. This can be done easily
using the calloc function.
CISC105 – Topic 12
The calloc Function




calloc is short for “contiguous allocation”.
This function takes two parameters, the
number of elements to allocate and the size
of one element.
It returns a pointer to the beginning of the
newly allocated memory block, thus this
pointer is equal to the address of the first
element of the array.
Note that this function also initializes every
element of the new array to zero.
CISC105 – Topic 12
The calloc Function


Once an array is dynamically allocated
in such a manner, it can be accessed
using the same operators and other
uses as a statically allocated array.
This can be shown in a simple example.
CISC105 – Topic 12
Dynamically Allocating
Arrays with calloc
int
char
int
*array_of_nums;
*string;
size_of_string, number_of_nums;
printf(“How many characters in the string?”);
scanf(“%d”,&size_of_string);
string = (char *)calloc(size_of_string, sizeof(char);
scanf(“%s”,string);
printf(“How many numbers?”);
scanf(“%d”,&number_of_nums);
array_of_nums =
(int *)calloc(number_of_nums,sizeof(int));
for (count = 0; count < number_of_nums; count++)
scanf(“%d”,&array_of_nums[count]);
CISC105 – Topic 12
Freeing Dynamically
Allocated Memory



Now we have seen that malloc and calloc
can be used to dynamically allocate memory.
When a static variable, i.e. a local variable of
a function, falls out of scope, for example,
when the function it is defined in finishes
(executes a return statement or the closing
brace), C automatically “frees” that memory.
This means the memory used for these
variables is deallocated (marked as free – it
can be used in the future when new memory
must be allocated).
CISC105 – Topic 12
Freeing Dynamically
Allocated Memory


This is NOT true of dynamically allocated
memory. While the pointer that we set
using malloc may fall out of scope and be
unallocated, the memory it points to (the
memory malloc allocated) is not freed.
In C, it is the responsibility of the
programmer to free any memory that is
dynamically allocated in the program.
CISC105 – Topic 12
The free Function




This is done using the free function.
It takes one parameter, the pointer returned by
malloc.
When this function is called, the memory
allocated by malloc at the address contained in
the pointer is deallocated (marked as free – it
can be used in the future when new memory
must be allocated).
Thus, malloc and free can be thought of as
companion functions. One allocates memory
when it is needed, the other frees memory when
it is no longer needed.
CISC105 – Topic 12
The free Function
char
int
*string;
size_of_string;
printf(“How many characters in the string?”);
scanf(“%d”,&size_of_string);
string = (char *)calloc(size_of_string, sizeof(char);
scanf(“%s”,string);
printf(“The string you typed in was:\n%s\n”, string);
free(string);
CISC105 – Topic 12
One Common Problem




One problem is quite common when using
dynamically allocated memory.
Sometimes, multiple pointers can point to the
same memory block.
If the free function is called with one of these
pointers, the memory block they both point to is
unallocated. Thus, if the second pointer (not the
one passed to free) is later used, the memory it
points to is no longer allocated.
This leads to the same problem as overstepping
the bounds of an array (attempting to use
memory that is not allocated).
CISC105 – Topic 12
One Common Problem
char
int
This statement sets string2 to string.
*string, *string2;
thus string2 now points to the same
size_of_string;
place in memory as string.
printf(“How many characters in the string?”);
scanf(“%d”,&size_of_string);
Here, we deallocate the memory
string = (char *)calloc(size_of_string,
string points to. As sizeof(char);
string2 points
string2 = string;
scanf(“%s”,string); to the same memory block as string
printf(“The string you
free(string);
does, string2 now points to
typedunallocated
in was:\n%s\n”,
string);
memory.
DANGEROUS!
. . .
CISC105 – Topic 12
The Linked List


Now that we understand the concept of
dynamic memory allocation, we can
look at a linked list.
A linked list is a data structure which is
a sequence of nodes in which each
node is linked, or connected to, the
node following it.
CISC105 – Topic 12
The Linked List
Data 1
Data 1
Data 2
Next node
Data 2
Next node
Data 3
Data 3
Next node
Data 4
Data 4
NO NEXT
CISC105 – Topic 12
The Linked List



In this linked list example, each node has two
pieces of data. Each node also has a pointer
to the next node.
So, we need two things to form a linked list:
a way to combine various datatypes and
variables together into one datatype and a
way to “point” to the next one of these
combination datatypes.
So…how can we accomplish this?
CISC105 – Topic 12
The Linked List



The first goal, combining various datatypes
and variables into one datatype, is easily
handled with a structure.
The second goal, being able to “point” to the
next structure is easily handled using
pointers.
So, we have all of the components we need
in order to construct a linked list.
CISC105 – Topic 12
Structures
with Pointer Components

We can form a linked list by defining a
structure with a pointer component.
typedef struct a_node {
char lastname[20];
float num_grade;
struct a_node *next_p;
} a_node2;
CISC105 – Topic 12
Structures
with Pointer Components
typedef struct a_node {
char lastname[20];
float num_grade;
struct a_node *next_p;
} a_node2;


The a_node name is known as a structure tag. What
is does is let us use the phrase struct a_node as
an alternative to using the work a_node2.
We use this as the datatype for the link pointer, as
the compiler has not yet reached the definition of the
word a_node2. It has reached the definition of the
phrase struct a_node, so we can use this as the
datatype.
CISC105 – Topic 12
Structures
with Pointer Components
typedef struct {
char lastname[20];
float num_grade;
a_node2 *next_p;
} a_node2;

This definition is illegal and will result in a
compile error. When the compiler reaches
the declaration of the link pointer, it does not
yet know what an a_node2 is. Thus, this will
cause a compiler error.
CISC105 – Topic 12
Structures
with Pointer Components
typedef struct a_node {
char lastname[20];
float num_grade;
struct a_node *next_p;
} a_node2;

Notice that when we use this structure
tag, we need to use the entire phrase
struct a_node and not simply
a_node.
CISC105 – Topic 12
Creating a Linked List

We can then setup a linked list using
these structures:
a_node2 *node1_p, *node2_p, *node3_p;
node1_p = (a_node2 *)malloc(sizeof(a_node2));
node2_p = (a_node2 *)malloc(sizeof(a_node2));
node3_p = (a_node2 *)malloc(sizeof(a_node2));
strcpy(node1_p->lastname, “Smith”);
strcpy(node2_p->lastname, “Jones”);
strcpy(node3_p->lastname, “Williams”);
node1_p->num_grade=90; node2_p->num_grade=20;
node3_p->num_grade=100;
node1_p->next_p = node2_p;
node2_p->next_p = node3_p;
node3_p->next_p = NULL;
CISC105 – Topic 12
Creating a Linked List
Address = 1080
Address = 2370
Jones
20
2370
Williams
100
NULL
Node1_p
Node2_p
Node3_p
1000
1080
2370
Address = 1000
Smith
90
1080
CISC105 – Topic 12
Creating a Linked List



Notice that if we have the pointer to the first
node in the linked list, we do not need the
pointers to the other nodes, as the first node
contains a pointer to the second node, the
second node contains a pointer to the third
node, etc.
Thus, we can access any node in the linked
list by starting at the first node and following
the link pointers.
Thus, we can think of a linked list as . . .
CISC105 – Topic 12
Creating a Linked List
Address = 1000
Smith
90
1080
Address = 1080
Address = 2370
Jones
20
2370
Williams
100
NULL
Node1_p
1000
CISC105 – Topic 12
Accessing the Data
Contained in a Linked List


So, we can see that node1_p points to
the first node in our linked list. This
first node is known as the head of the
linked list.
We can access the data in the first node
by using the “->” operator:
printf(“Name:%s\n”,node1_p->lastname);
printf(“Grade:%f\n”,node1_p->num_grade);
CISC105 – Topic 12
Accessing the Data
Contained in a Linked List

The first node also contains a pointer to the
second node. Therefore, we can access this
pointer and use it to access the data in the
second node:
printf(“Name?”);
scanf(“%s”,node1_p->next_p->lastname);
printf(“Grade?”);
scanf(“%f”,&(node1_p->next_p->num_grade));
printf(“Name:%s”,node1_p->next_p->lastname);
printf(“Grade:%f”,node1_p->next_p->num_grade);
CISC105 – Topic 12
Accessing the Data
Contained in a Linked List


We can access the data contained in
nodes past the second node in the same
manner; we can simply follow the pointers
to the “next node” until we arrive at the
node that contains the data we want to
access.
Thus, if the address stored in node1_p is
known by a function, that function can
access every element (node) of the linked
list.
CISC105 – Topic 12
The Tail Node



Notice in the picture that the last node in
the list had its link pointer set to NULL.
Thus, we can test to see if we are at the
last node in a linked list.
If the link pointer of a node is set to NULL,
we are at the end of the list. If the link
pointer of a node is not set to NULL, it will
point to the next node in the list.
CISC105 – Topic 12
The Tail Node



In this way, the NULL pointer is used to
indicate the end of the linked list.
The last node in the linked list is called
the tail of the list.
So, the head of the list is the first node,
the tail of the list is the last node, and
we can tell that we are at the tail of the
list if the link pointer is set to NULL.
CISC105 – Topic 12
Linked List Advantages


Linked lists provide a number of
advantages.
First and foremost, they are perfectly sized
for the data that they store. When a data
record is encountered, a node to store it is
allocated. This defeats the limitations of
arrays, where we needed to know the
array size at program compile time.
CISC105 – Topic 12
Linked List Advantages


As linked lists are “linked” using
pointers, it is easy to manipulate them.
Two common operations that can be
easily performed on linked lists are the
insertion of a node into the list (at some
point) and the deletion of a node from
the list.
CISC105 – Topic 12
Linked List Advantages:
Inserting a Node into the List
Address = 1000
Smith
90
1080
Address = 1080
Address = 2370
Jones
20
2370
8460
Williams
100
NULL
Address = 8460
Node1_p
1000
CISC105 – Topic 12
Norton
98
2370
Linked List Advantages:
Deleting a Node from the List
Address = 1000
Smith
90
1080
2370
Address = 1080
Address = 2370
Jones
20
2370
Williams
100
NULL
Node1_p
1000
CISC105 – Topic 12
Linked List Traversal


A common operation on a linked list is
traversing a list. This means that we
keep following the link pointers until we
arrive at the end of the list.
We can write a function that traverses
the linked list we have previously made,
displaying the data stored in each node.
CISC105 – Topic 12
Linked List Traversal
void display_list(a_node2 *head_p)
{
a_node2 *current_p;
if (head_p == NULL)
printf(“Empty List!”);
else
{
current_p = head_p;
while (current_p != NULL)
{
printf(“%s:”,current_p->lastname);
printf(“%d\n”,current_p->num_grade);
current_p = current_p->next_p;
}
}
}
CISC105 – Topic 12
Linked List Traversal

We can also write a function that
searches a linked list for a target data
item, in this case, a last name.
CISC105 – Topic 12
Linked List Traversal
a_node2 * serach_list(a_node2 *head_p, char target[])
{
a_node2 *current_p;
for(current_p = head_p;
current_p != NULL &&
strcmp(current_p->lastname,target) != 0;
current_p = current_p->next)
{ }
return current_p;
}
CISC105 – Topic 12