CSE 2421: Systems I Low-Level Programming and Computer Organization Dynamic Memory Allocation and Linked Lists Presentation I Read/Study: Reek Chapter 11 & 12 Gojko Babić 02-26-2017 Functions malloc and free void *malloc(size_t size); allocates size bytes and returns a pointer to the allocated memory. The memory is not cleared. a returned pointer to the allocated memory is suitably aligned for any kind of variable. On error, this function return NULL. void free(void *ptr); frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(). Otherwise, or if free(ptr) has already been called before, undefined behavior occurs. If ptr is NULL, no operation is performed. this function returns no value. g. babic Presentation I 2 1
Using malloc Function Here how a new "nameless" variable of type int can be created with a pointer addressing (pointing) that variable: int *p1; p1 = malloc(sizeof(int)); the new variable is referred to as *p1 and *p1 can be used anyplace an integer variable can, e.g. *p1=201; x = *p1 + 7; printf( %d, *p1); These type of variables are called dynamic variables: they are created while the program is running, an area of memory called the heap is reserved for dynamic variables; new dynamic variables use memory in the heap, if all of the heap is used, calls to malloc returns NULL. g. babic Presentation I 3 Using free Function When dynamic variables are no longer needed, they can be deleted and the memory they used is returned to the heap. To delete dynamic memory use free function: free (p); the memory previously allocated by p = malloc ( ) is back in the heap and the value of p is now undefined, i.e. it is not pointing to any memory. If another pointer variable was pointing to this dynamic variable, that pointer is also undefined Undefined pointer variables are called dangling pointers referencing a dangling pointer is usually disastrous for any running program g. babic Presentation I 4 2
Linux x86-64 Program Run-Time Memory Image 2 48 1 Kernel virtual memory User stack (created at runtime) Memory invisible to user code %rsp Run-time heap (created by malloc) 0x400000 Read/write data Read-only code and data Loaded from the executable file 0 bryant Presentation I 5 Simple Dynamic Memory Allocation int main() int *p1, *p2; p1 = malloc(sizeof(int)); *p1 = 42; p2 = p1; printf("\n*p1= %d *p2= %d", *p1, *p2); *p2=53; printf("\n*p1= %d *p2= %d", *p1, *p2); p1 = malloc(sizeof(int)); *p1 =88; printf("\n*p1= %d *p2= %d", *p1, *p2); Output: *p1= 42 *p2= 42 *p1= 53 *p2= 53 *p1= 88 *p2= 53 g. babic Presentation I 6 3
Simple Dynamic Memory Allocation (cont.) p1 = malloc(sizeof(int)); p1 = malloc(sizeof(int)); g. babic Presentation I 7 Dynamic Arrays Normal arrays require that the programmer determine the size of the array when the program is written and if the programmer estimates too large, memory is wasted, if the programmer estimates too small, the program may not work in some situations. A dynamic array is an array whose size is determined when the program is running, not when you write the program. A dynamic array of N elements of type double can be created this way: double *d; d = malloc(i*sizeof(double)); i is an int variable with positive value N, d can now be used as if it were an ordinary array, e.g. d[0], and d[j], or *d and *(d+j). When done with the array, its allocated memory should be returned to the heap; it is done like this: free(d); g. babic Presentation I 8 4
Dynamic 1D-Array of Structures: Version A struct stype char cx; int iy; ; int main() int i, N=10; struct stype *ptr; ptr = malloc(n*sizeof(struct stype)); // ptr==null? for (i=0; i<n; i++) if(i%2==0) ptr[i].cx='a'; else ptr[i].cx='b'; ptr[i].iy=i; for (i=0; i<n; i++) printf("\n %c %d", (ptr+i)->cx, (ptr+i)->iy); Output: A 0 B 1 A 2 B 3 A 4 B 5 A 6 B 7 A 8 B 9 The printf could be also written as: printf("\n %c %d", ptr[i].cx, ptr[i].iy); or printf("\n %c %d", (*(ptr+i)).cx, (*(ptr+i)).iy); g. babic Presentation I 9 Dynamic 1D-Array of Structures: Version B typedef struct char cx; int iy; stype; int main() int i, N=10; stype *ptr; ptr = malloc(n*sizeof(stype)); // ptr==null? for (i=0; i<n; i++) if(i%2==0) ptr[i].cx='a'; else ptr[i].cx='b'; ptr[i].iy=i; for (i=0; i<n; i++) printf("\n %c %d", (ptr+i)->cx, (ptr+i)->iy); ptr[i].cx= A ; could be written as: (*(ptr+i)).cx= A ; or (ptr+i)->cx= A ; Output: A 0 B 1 A 2 B 3 A 4 B 5 A 6 B 7 A 8 B 9 g. babic Presentation I 10 5
Dynamic 2-Dimentional Array: A Version #define COLS 5 int main(void) int (*rptr)[cols]; int nrows = 10; int i, j; rptr = malloc(nrows * COLS * sizeof(int)); if (rptr == NULL) printf( malloc didn t work ); return 0; for (i = 0; i < nrows; i++) for (j = 0; j < COLS; j++) rptr[i][j] = (i+1)*(j+1); // *(*(rptr+i)+j)) = (i+1)*(j+1); for (i = 0; i < nrows; i++) for (j = 0; j < COLS; j++) if(j==0)printf("\n"); printf("%8d", rptr[i][j]); // printf("%8d", *(*(rptr+i)+j)); return 0; Output: 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25 6 12 18 24 30 7 14 21 28 35 8 16 24 32 40 9 18 27 36 45 10 20 30 40 50 g. babic Presentation I 11 Dynamic 2-Dimentional Array: B Version #define COLS 5 typedef int RowArray[COLS]; int main(void) RowArray *rptr; int nrows = 10; int i, j; rptr = malloc(nrows * COLS * sizeof(int)); if (rptr == NULL) printf( malloc didn t work ); return 0; for (i = 0; i < nrows; i++) for (j = 0; j < COLS; j++) rptr[i][j] = (i+1)*(j+1); for (i = 0; i < nrows; i++) for (j = 0; j < COLS; j++) if(j==0)printf("\n"); printf("%8d", rptr[i][j]); // printf("%8d", *(*(rptr+i)+j)); return 0; Output: 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25 6 12 18 24 30 7 14 21 28 35 8 16 24 32 40 9 18 27 36 45 10 20 30 40 50 g. babic Presentation I 12 6
Nodes and Linked Lists A linked list is a list that can grow and shrink while the program is running and it is constructed using pointers, A linked list consists of structures that contain a pointer variable connecting them to other dynamic variables, A linked list can be visualized as items, drawn as boxes, connected to other items by arrows: nodes contain the data item(s) and a pointer that points to another node of the same type Example: typedef struct node char item[10]; int count; struct node *link; ListNode; This circular definition is allowed in C g. babic Presentation I 13 Nodes and Pointers The box labeled head is not a node, but a pointer variable pointing to the first node. Pointer variable head is declared as: ListNode* head; The boxes in the drawing represent the nodes of a linked list. The arrows in the drawing represent pointers. To change the number in the first node from 10 to 12, use this: (*head).count = 12; // assumed that head points to the first node. g. babic Presentation I 14 7
In the statement: (*head).count = 12; head is a pointer variable, so *head is the node that head points to; the parentheses are necessary because the dot operator (.) has higher precedence than the dereference operator (*). The arrow operator > combines the actions of the dereferencing operator and the dot operator to specify a member of a struct to by a pointer, e.g. the statement above can be written as head >count = 12; The defined constant NULL is used as an end marker for a linked list. A program can step through a list of nodes by following the pointers, but when it finds a node containing NULL, it knows it has come to the end of the list. g. babic Accessing Items in Node Presentation I 15 Accessing Items in Node (continued) head->count = 12; strcpy(head->item, bagels ); A linked list is a list of nodes in which each node has a member variable that is a pointer that points to the next node in the list. The pointer variable head, points to the first node. The last node contains a pointer set to NULL g. babic Presentation I 16 8
Building a Linked List Let's use a simple node definition: typedef struct node int data; struct node *link; ListNode; Now, we can declare the pointer variable head: ListNode *head; head is a pointer variable that will point to the first node of the list when the node is created. Next, we create the first node: head = malloc(sizeof(listnode)); head points to the first, and only, node in the list. Since head points to a node, we could give values to the member variables of the node: head >data = 3; head >link = NULL; //since this is the last node g. babic Presentation I 17 Empty Lists and Memory Leaks A list with nothing in it is called an empty list; that list has no nodes. The head pointer of an empty list is NULL, and any function written to manipulate a linked list should check to see if it works on the empty list. You might be tempted to add new node at the beginning of the list using the head pointer to construct another node: head = malloc(sizeof(listnode)); head >data = 12; But the node(s) that head used to point to is (are) now lost! Nodes that are lost by assigning their pointers new addresses are not accessible any longer and the program has no way to refer to those nodes even to free them and return to the heap. Programs that loose nodes have a memory leak and significant memory leaks can cause program crashes. g. babic Presentation I 18 9
Adding a Node at the Head of List 1. Set up new node: ListNode* temp_ptr; temp_ptr = malloc(sizeof(listnode)); temp_ptr->data = 12; g. babic 19 Locating a Node in a Linked List typedef struct node int data; struct node *link; ListNode; ListNode* PointToMe(ListNode *ptr, int target); /* This function returns a pointer to the node with data==target, or NULL if such doesn t exist; ptr should be the pointer to the first node of the list */ ListNode* PointToMe(ListNode *here, int target) if(here == NULL) return NULL; while (here->data!= target && here->link!=null) here= here->link; if(here->data == target) return here; return NULL; Presentation I 20 10
Locating a Node in a Linked List (cont.) 1. The list before the function call 2. After the function call: PointToMe (head, 6) target = 6 g. babic 5. Function returns value of pointer here 21 Inserting in the Middle of a Linked List 1. find pointer to a node after which new node should be inserted; let it be the pointer after_me; 2. create new node and update its value, such as: temp_ptr = malloc(sizeof(listnode)); temp_ptr->data=5; 3. Inserting: temp_ptr->link=after_me->link; after_me->link=temp_ptr; g. babic Presentation I 22 11
Removing a Node from a Linked List free(discard); If the node to be removed is the first node in the list: if (head == discard) head = discard >link; free(discard); g. babic Presentation I 23 Summary of Insert and Remove Inserting into a linked list requires only to change two pointers: inserting into a linked list is often more efficient than inserting into an array; using an array for the list would involve copying as many as all of the array elements to new locations to make room for the new item. To remove a node from a linked list: position one pointer to point at the node prior to the node to remove and position another pointer to point at the node to remove, perform: before >link = discard >link; return to the heap: free (discard); removing from a linked list is often more efficient than deleting from an array; using an array for the list would involve copying of all array elements to new locations to occupy room for the deleted item. g. babic Presentation I 24 12
A Double Linked List The node structure for a double linked list could be as follows: typedef struct node struct node *back_link; int data; struct node * forward_link; ListNode; g. babic Presentation I 25 Binary Tree The node structure for a binary tree could be as follows: typedef struct node int data; struct node * left_link; struct node *right_link; ListNode; g. babic Presentation I 26 13