Presentation is loading. Please wait.

Presentation is loading. Please wait.

Topic 12 – Dynamic Memory Allocation & Linked Lists

Similar presentations


Presentation on theme: "Topic 12 – Dynamic Memory Allocation & Linked Lists"— Presentation transcript:

1 Topic 12 – Dynamic Memory Allocation & Linked Lists

2 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

3 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

4 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

5 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

6 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

7 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

8 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. CISC105 – Topic 12

9 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

10 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

11 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

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

13 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

14 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

15 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

16 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

17 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

18 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

19 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 *) *num1_p = 27; *num2_p = 99; (*record1_p).letter_grade = ‘A’; strcpy(record2_p->last,”Six”); strcpy(record2_p->first,”Jeffrey”); CISC105 – Topic 12

20 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

21 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

22 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

23 Dynamically Allocating Arrays 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]); CISC105 – Topic 12

24 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

25 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

26 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

27 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); CISC105 – Topic 12

28 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

29 One Common Problem This statement sets string2 to string.
thus string2 now points to the same place in memory as string. 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); . . . Here, we deallocate the memory string points to. As string2 points to the same memory block as string does, string2 now points to unallocated memory. DANGEROUS! CISC105 – Topic 12

30 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

31 The Linked List Data 1 Data 1 Data 2 Next node Data 2 Data 3 Next node
NO NEXT CISC105 – Topic 12

32 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

33 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

34 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

35 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

36 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

37 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

38 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

39 Creating a Linked List Node1_p Node2_p Node3_p Address = 1000 1000
1080 Address = 2370 2370 Smith Jones Williams 90 20 100 1080 2370 NULL Node1_p Node2_p Node3_p CISC105 – Topic 12

40 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

41 Creating a Linked List Node1_p Address = 1000 1000 Address = 1080
Smith Jones Williams 90 20 100 1080 2370 NULL Node1_p CISC105 – Topic 12

42 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

43 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

44 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

45 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

46 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

47 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

48 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

49 Linked List Advantages: Inserting a Node into the List
Address = 1000 1000 Address = 1080 Address = 2370 Smith Jones Williams 90 20 100 1080 8460 2370 NULL Address = 8460 Norton 98 Node1_p 2370 CISC105 – Topic 12

50 Linked List Advantages: Deleting a Node from the List
Address = 1000 1000 Address = 1080 Address = 2370 Smith Jones Williams 90 20 100 2370 1080 2370 NULL Node1_p CISC105 – Topic 12

51 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

52 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

53 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

54 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


Download ppt "Topic 12 – Dynamic Memory Allocation & Linked Lists"

Similar presentations


Ads by Google