topic 12 – dynamic memory allocation & linked lists

54
Topic 12 – Dynamic Memory Allocation & Linked Lists

Post on 20-Dec-2015

253 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Topic 12 – Dynamic Memory Allocation & Linked Lists

Topic 12 – Dynamic Memory Allocation & Linked Lists

Page 2: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

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.

Page 3: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 4: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 5: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 6: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 7: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 8: Topic 12 – Dynamic Memory Allocation & Linked Lists

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-by-reference. The function can modify the array.

Page 9: Topic 12 – Dynamic Memory Allocation & Linked Lists

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;

Page 10: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 11: Topic 12 – Dynamic Memory Allocation & 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.

Page 12: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 13: Topic 12 – Dynamic Memory Allocation & Linked Lists

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).

Page 14: Topic 12 – Dynamic Memory Allocation & Linked Lists

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));

Page 15: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 16: Topic 12 – Dynamic Memory Allocation & Linked Lists

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));

Page 17: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

The malloc Function

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.

student_record *TopStudent_p;

TopStudent_p =

(student_record *)malloc(sizeof(student_record));

Page 18: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

ReferencingDynamically 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.

Page 19: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

ReferencingDynamically 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,”Six”);strcpy(record2_p->first,”Jeffrey”);

Page 20: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 21: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 22: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 23: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

Dynamically AllocatingArrays with calloc

int *array_of_nums;char *string;int 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]);

Page 24: Topic 12 – Dynamic Memory Allocation & Linked Lists

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).

Page 25: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

Freeing DynamicallyAllocated 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.

Page 26: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 27: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

The free Function

char *string;int 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);

Page 28: Topic 12 – Dynamic Memory Allocation & Linked Lists

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).

Page 29: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

One Common Problem

char *string, *string2;int size_of_string;

printf(“How many characters in the string?”);scanf(“%d”,&size_of_string);string = (char *)calloc(size_of_string, sizeof(char);string2 = string;scanf(“%s”,string);

printf(“The string you typed in was:\n%s\n”, string);free(string);

. . .

This statement sets string2 to string.thus string2 now points to the same

place in memory as string.

Here, we deallocate the memorystring points to. As string2 points

to the same memory block as stringdoes, string2 now points to

unallocated memory.DANGEROUS!

Page 30: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 31: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

The Linked List

Data 1

Next node

Data 1 Data 2

Next node

Data 2 Data 3

Next node

Data 3

Data 4

NO NEXT

Data 4

Page 32: Topic 12 – Dynamic Memory Allocation & Linked Lists

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?

Page 33: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 34: Topic 12 – Dynamic Memory Allocation & Linked Lists

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;

Page 35: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

Structureswith Pointer Components

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.

typedef struct a_node {char lastname[20];float num_grade;struct a_node *next_p;

} a_node2;

Page 36: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

Structureswith Pointer Components

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.

typedef struct {char lastname[20];float num_grade;a_node2 *next_p;

} a_node2;

Page 37: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

Structureswith Pointer Components

Notice that when we use this structure tag, we need to use the entire phrase struct a_node and not simply a_node.

typedef struct a_node {char lastname[20];float num_grade;struct a_node *next_p;

} a_node2;

Page 38: Topic 12 – Dynamic Memory Allocation & Linked Lists

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;

Page 39: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

Address = 2370

2370

Address = 1080

1080

Address = 1000

1000

Creating a Linked List

Node1_p Node3_pNode2_p

Smith WilliamsJones

90 20 100

1080 2370 NULL

Page 40: Topic 12 – Dynamic Memory Allocation & Linked Lists

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 . . .

Page 41: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

Creating a Linked List

Address = 2370Address = 1080Address = 1000

1000

Node1_p

Smith WilliamsJones

90 20 100

1080 2370 NULL

Page 42: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

Accessing the DataContained 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);

Page 43: Topic 12 – Dynamic Memory Allocation & Linked Lists

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);

Page 44: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

Accessing the DataContained 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.

Page 45: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 46: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 47: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 48: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 49: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

Linked List Advantages:Inserting a Node into the List

Address = 2370Address = 1080Address = 1000

1000

Node1_p

Smith WilliamsJones

90 20 100

1080 2370 NULL

Address = 8460

Norton

98

2370

8460

Page 50: Topic 12 – Dynamic Memory Allocation & Linked Lists

CISC105 – Topic 12

Linked List Advantages:Deleting a Node from the List

Address = 2370Address = 1080Address = 1000

1000

Node1_p

Smith WilliamsJones

90 20 100

1080 2370 NULL2370

Page 51: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 52: Topic 12 – Dynamic Memory Allocation & Linked Lists

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;

} }}

Page 53: Topic 12 – Dynamic Memory Allocation & Linked Lists

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.

Page 54: Topic 12 – Dynamic Memory Allocation & Linked Lists

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;

}