7-Dynamics_Memory PDF
Document Details
Uploaded by ModernFantasticArt
NYU
Tags
Summary
This PDF document discusses dynamic data structures, nodes, pointers, and memory allocation in programming, particularly in C. It covers topics like heap and stack, highlighting the use of malloc and memory management.
Full Transcript
Dynamic Data Structures dynamic data structure a structure that can expand and contract as a program executes nodes dynamically allocated structures that are linked together to form a composite structure...
Dynamic Data Structures dynamic data structure a structure that can expand and contract as a program executes nodes dynamically allocated structures that are linked together to form a composite structure 2 Reminder - Pointers 3 Pointers as Function Arguments 4 Pointers as Return Types 1523198053 1187214107 1108300978 430494959 1421301276 930971084 123250484 106932140 1604461820 149169022 *(p + ) : 1523198053 *(p + ) : 1187214107 *(p + ) : 1108300978 *(p + ) : 430494959 *(p + ) : 1421301276 *(p + ) : 930971084 *(p + ) : 123250484 *(p + ) : 106932140 *(p + ) : 1604461820 *(p + ) : 149169022 5 6 Function Pointers A pointer to a function 1) Unlike normal pointers, a function pointer points to code, not data. Typically a function pointer stores the start of executable code. 2) Unlike normal pointers, we do not allocate de- allocate memory using function pointers. Value of a is 10 7 Function Pointers Function pointers provide a way of passing around instructions for how to do something You can write flexible functions and libraries that allow the programmer to choose behavior by passing function pointers as arguments 8 Dynamic Memory Allocation heap region of memory in which function malloc dynamically allocates blocks of storage stack region of memory in which function data areas are allocated and reclaimed Both in RAM 9 Stack vs Heap Stack: Variables created on the stack will go out of scope and are automatically deallocated. Much faster to allocate in comparison to variables on the heap. Implemented with an actual stack data structure. Stores local data, return addresses, used for parameter passing. Can have a stack overflow when too much of the stack is used (mostly from infinite or too deep recursion, very large allocations). Data created on the stack can be used without pointers. You would use the stack if you know exactly how much data you need to allocate before compile time and it is not too big. Usually has a maximum size already determined when your program starts. 10 Stack vs Heap Heap: In C C++, variables on the heap must be destroyed manually and never fall out of scope. The data is freed with free. Slower to allocate in comparison to variables on the stack. Used on demand to allocate a block of data for use by the program. Can have fragmentation when there are a lot of allocations and deallocations. In C++ or C, data created on the heap will be pointed to by pointers and allocated with new or malloc respectively. Can have allocation failures if too big of a buffer is requested to be allocated. You would use the heap if you don't know exactly how much data you will need at run time or if you need to allocate a lot of data. Responsible for memory leaks. 11 12 13 14 no malloc after free undefined behaviour 15 16 17 18 19 Linked Lists linked list a sequence of nodes in which each node but the last contains the address of the next node empty list a list of no nodes represented in C by the pointer NULL, whose value is zero list head the first element in a linked list 20 how do you remove colored bead? how do you add one? 21 in all nodes but the last, link pointer contains the address of the next node we do not know how many elements we have, dynamic allocation is required If you create a node struct, how should it look like? 22 Linked Lists A linked list is a linear collection of self-referential structures, called nodes, connected by pointer links—hence, the term “linked” list. A linked list is accessed via a pointer to the first node of the list. Subsequent nodes are accessed via the link pointer member stored in each node. By convention, the link pointer in the last node of a list is set to NULL to mark the end of the list. Data is stored in a linked list dynamically—each node is created as necessary. Linked Lists A node can contain data of any type including other struct objects. Stacks and queues are also linear data structures Constrained versions of linked lists Trees are nonlinear data structures. Lists of data can be stored in arrays, but linked lists provide several advantages. A linked list is appropriate when the number of data elements is unpredictable. Linked Lists Linked lists are dynamic, so the length of a list can increase or decrease as necessary. The size of an array created at compile time, however, cannot be altered. Arrays can become full. Linked lists become full only when the system has insufficient memory to satisfy dynamic storage allocation requests. Linked Lists Linked lists can be maintained in sorted order by inserting each new element at the proper point in the list. Linked-list nodes are normally not stored contiguously in memory. Logically, however, the nodes of a linked list appear to be contiguous. Linked List Example Write a code that manipulates a list of characters. You can insert a character in the list in alphabetical order (function insert) or delete a character from the list (function delete). © 2016 Pearson Education, Ltd. All rights reserved. © 2016 Pearson Education, Ltd. All rights reserved. Linked List The primary functions of linked lists are insert and delete Function isEmpty is a predicate function—it does not alter the list in any way; rather it determines whether the list is empty (i.e., the pointer to the first node of the list is NULL). If the list is empty, 1 is returned; otherwise, 0 is returned. Function printList prints the list. Linked List Characters are inserted in the list in alphabetical order. Function insert receives the address of the list and a character to be inserted. The list’s address is necessary when a value is to be inserted at the start of the list. Providing the address enables the list (i.e., the pointer to the first node of the list) to be modified via a call by reference. Because the list itself is a pointer (to its first element), passing its address creates a pointer to a pointer (i.e., double indirection). This is a complex notion and requires careful programming. Linked List Steps for inserting a character in the list : Create a node: call malloc, assign to newPtr the address of the allocated memory, assign the character to be inserted to newPtr->data, and assign NULL to newPtr->nextPtr Initialize previousPtr to NULL and currentPtr to *sPtr—the pointer to the start of the list. These pointers store the locations of the node preceding the insertion point and the node after the insertion point. While currentPtr is not NULL and the value to be inserted is greater than currentPtr->data, assign currentPtr to previousPtr and advance currentPtr to the next node in the list This locates the insertion point for the value. Linked List If previousPtr is NULL, insert the new node as the first node in the list. Assign *sPtr to newPtr->nextPtr (the new node link points to the former first node) and assign newPtr to *sPtr (*sPtr points to the new node). Otherwise, if previousPtr is not NULL, the new node is inserted in place. Assign newPtr to previousPtr->nextPtr (the previous node points to the new node) and assign currentPtr to newPtr->nextPtr (the new node link points to the current node). © 2016 Pearson Education, Ltd. All rights reserved. Linked List Figure 12.5 illustrates the insertion of a node containing the character 'C' into an ordered list. Part (a) of the figure shows the list and the new node just before the insertion. Part (b) of the figure shows the result of inserting the new node. The reassigned pointers are dotted arrows. For simplicity, we implemented function insert (and other similar functions in this chapter) with a void return type. It’s possible that function malloc will fail to allocate the requested memory. In this case, it would be better for our insert function to return a status that indicates whether the operation was successful. Function delete Function delete receives the address of the pointer to the start of the list and a character to be deleted. Steps for deleting a character from the list: If the character to be deleted matches the character in the first node of the list, assign *sPtr to tempPtr (tempPtr will be used to free the unneeded memory), assign (*sPtr)->nextPtr to *sPtr (*sPtr now points to the second node in the list), free the memory pointed to by tempPtr, and return the character that was deleted. Otherwise, initialize previousPtr with *sPtr and initialize currentPtr with (*sPtr)->nextPtr to advance the second node. While currentPtr is not NULL and the value to be deleted is not equal to currentPtr->data, assign currentPtr to previousPtr, and assign currentPtr->nextPtr to currentPtr. This locates the character to be deleted if it’s contained in the list. Function delete If currentPtr is not NULL, assign currentPtr to tempPtr, assign currentPtr->nextPtr to previousPtr- >nextPtr, free the node pointed to by tempPtr, and return the character that was deleted from the list. If currentPtr is NULL, return the null character ('\0') to signify that the character to be deleted was not found in the list. © 2016 Pearson Education, Ltd. All rights reserved. Function delete Fig. 12.6 illustrates the deletion of a node from a linked list. Part (a) of the figure shows the linked list after the preceding insert operation. Part (b) shows the reassignment of the link element of previousPtr and the assignment of currentPtr to tempPtr. Pointer tempPtr is used to free the memory allocated to the node that stores 'C'. Recall that we recommended setting a freed pointer to NULL. We do not do that in these two cases, because tempPtr is a local automatic variable and the function returns immediately. Function printList Function printList receives a pointer to the start of the list as an argument and refers to the pointer as currentPtr. The function first determines whether the list is empty and, if so, prints “List is empty." and terminates. Otherwise, it prints the data in the list Linked List While currentPtr is not NULL, the value of currentPtr->data is printed by the function, and currentPtr->nextPtr is assigned to currentPtr to advance to the next node. If the link in the last node of the list is not NULL, the printing algorithm will try to print past the end of the list, and an error will occur. The printing algorithm is identical for linked lists, stacks and queues. Stacks A stack can be implemented as a constrained version of a linked list. New nodes can be added to a stack and removed from a stack only at the top. For this reason, a stack is referred to as a last-in, first-out (LIFO) data structure. A stack is referenced via a pointer to the top element of the stack. The link member in the last node of the stack is set to NULL to indicate the bottom of the stack. Stacks Figure 12.7 illustrates a stack with several nodes—stackPtr points to the stack’s top element. Stacks and linked lists are represented identically. The difference between stacks and linked lists is that insertions and deletions may occur anywhere in a linked list, but only at the top of a stack. © 2016 Pearson Education, Ltd. All rights reserved. Stacks The primary functions used to manipulate a stack are push and pop. Function push creates a new node and places it on top of the stack. Function pop removes a node from the top of the stack, frees the memory that was allocated to the popped node and returns the popped value. Let’s implement a simple stack of integers. The program provides three options: 1) push a value onto the stack (function push), 2) pop a value off the stack (function pop) and 3) terminate the program. Stack Example Let’s assume we have a character array called stack_s How can we write push, pop and retrieve functions? Push: pushes item and increments the subscript of the element at top of the stack Pop: removes top item and decrements the subscript of the element at top of the stack Retrieve: Accesses the element at the top of the stack without removing it. 50 51 52 53 NOW DYNAMICALLY HOA 54 © 2016 Pearson Education, Ltd. All rights reserved. © 2016 Pearson Education, Ltd. All rights reserved. Function push Function push places a new node at the top of the stack. The function consists of three steps: Create a new node by calling malloc and assign the location of the allocated memory to newPtr. Assign to newPtr->data the value to be placed on the stack and assign *topPtr (the stack top pointer) to newPtr->nextPtr—the link member of newPtr now points to the previous top node. Assign newPtr to *topPtr—*topPtr now points to the new stack top. Function push Manipulations involving *topPtr change the value of stackPtr in main. Figure 12.10 illustrates function push. Part (a) of the figure shows the stack and the new node before the push operation. The dotted arrows in part (b) illustrate Steps 2 and 3 of the push operation that enable the node containing 12 to become the new stack top. Function pop Function pop removes a node from the top of the stack. Function main determines if the stack is empty before calling pop. The pop operation consists of five steps: Assign *topPtr to tempPtr, which will be used to free the unneeded memory Assign (*topPtr)->data to popValue to save the value in the top node Assign (*topPtr)->nextPtr to *topPtr so *topPtr contains address of the new top node Free the memory pointed to by tempPtr Return popValue to the caller Function pop Figure 12.11 illustrates function pop. Part (a) shows the stack after the previous push operation. Part (b) shows tempPtr pointing to the first node of the stack and topPtr pointing to the second node of the stack. Function free is used to free the memory pointed to by tempPtr. © 2016 Pearson Education, Ltd. All rights reserved. Applications of Stacks Stacks have many interesting applications. For example, whenever a function call is made, the called function must know how to return to its caller, so the return address is pushed onto a stack. If a series of function calls occurs, the successive return values are pushed onto the stack in last-in, first-out order so that each function can return to its caller. Applications of Stacks Stacks support recursive function calls in the same manner as conventional nonrecursive calls. Stacks contain the space created for automatic variables on each invocation of a function. When the function returns to its caller, the space for that function's automatic variables is popped off the stack, and these variables no longer are known to the program. Stacks are used by compilers in the process of evaluating expressions and generating machine-language code. Queues Another common data structure is the queue. A queue is similar to a checkout line in a grocery store—the first person in line is serviced first, and other customers enter the line only at the end and wait to be serviced. Queue nodes are removed only from the head of the queue and are inserted only at the tail of the queue. For this reason, a queue is referred to as a first-in, first-out (FIFO) data structure. The insert and remove operations are known as enqueue and dequeue, respectively. Queues Queues have many applications in computer systems. For computers that have only a single processor, only one user at a time may be serviced. Entries for the other users are placed in a queue. Each entry gradually advances to the front of the queue as users receive service. The entry at the front of the queue is the next to receive service. Queues Queues are also used to support print spooling. A multiuser environment may have only a single printer. Many users may be generating outputs to be printed. If the printer is busy, other outputs may still be generated. These are spooled to disk where they wait in a queue until the printer becomes available. Queues Information packets also wait in queues in computer networks. Each time a packet arrives at a network node, it must be routed to the next node on the network along the path to its final destination. The routing node routes one packet at a time, so additional packets are enqueued until the router can route them. Figure 12.12 illustrates a queue with several nodes. Note the pointers to the head of the queue and the tail of the queue. © 2016 Pearson Education, Ltd. All rights reserved. 75 76 77 © 2016 Pearson Education, Ltd. All rights reserved. © 2016 Pearson Education, Ltd. All rights reserved. 80 Function enqueue Function enqueue receives three arguments from main: the address of the pointer to the head of the queue, the address of the pointer to the tail of the queue and the value to be inserted in the queue. Queues The function consists of three steps: To create a new node: Call malloc, assign the allocated memory location to newPtr, assign the value to be inserted in the queue to newPtr->data and assign NULL to newPtr->nextPtr If the queue is empty, assign newPtr to *headPtr, because the new node will be both the head and tail of the queue; otherwise, assign pointer newPtr to (*tailPtr)->nextPtr, because the new node will be placed after the previous tail node. Assign newPtr to *tailPtr, because the new node is the queue’s tail. Queues Figure 12.15 illustrates an enqueue operation. Part (a) shows the queue and the new node before the operation. The dotted arrows in part (b) illustrate Steps 2 and 3 of function enqueue that enable a new node to be added to the end of a queue that is not empty. © 2016 Pearson Education, Ltd. All rights reserved. Function dequeue Function dequeue receives the address of the pointer to the head of the queue and the address of the pointer to the tail of the queue as arguments and removes the first node from the queue. Function dequeue The dequeue operation consists of six steps: Assign (*headPtr)->data to value to save the data Assign *headPtr to tempPtr, which will be used to free the unneeded memory Assign (*headPtr)->nextPtr to *headPtr so that *headPtr now points to the new first node in the queue If *headPtr is NULL, assign NULL to *tailPtr because the queue is now empty. Free the memory pointed to by tempPtr Return value to the caller Queues Figure 12.16 illustrates function dequeue. Part (a) shows the queue after the preceding enqueue operation. Part (b) shows tempPtr pointing to the dequeued node, and headPtr pointing to the new first node of the queue. Function free is used to reclaim the memory pointed to by tempPtr. © 2016 Pearson Education, Ltd. All rights reserved. Trees Linked lists, stacks and queues are linear data structures. A tree is a nonlinear, two-dimensional data structure with special properties. Tree nodes contain two or more links. Trees This section discusses binary trees - trees whose nodes all contain two links (none, one, or both of which may be NULL). The root node is the first node in a tree. Each link in the root node refers to a child. The left child is the first node in the left subtree, and the right child is the first node in the right subtree. The children of a node are called siblings. A node with no children is called a leaf node. Computer scientists normally draw trees from the root node down— exactly the opposite of trees in nature. © 2016 Pearson Education, Ltd. All rights reserved. Trees In this section, a special binary tree called a binary search tree is created. A binary search tree (with no duplicate node values) has the characteristic that the values in any left subtree are less than the value in its parent node, and the values in any right subtree are greater than the value in its parent node. Next figure illustrates a binary search tree with 12 values. The shape of the binary search tree that corresponds to a set of data can vary, depending on the order in which the values are inserted into the tree. Function insertNode The functions used in our program to create a binary search tree and traverse the tree are recursive. Function insertNode receives the address of the tree and an integer to be stored in the tree as arguments. A node can be inserted only as a leaf node in a binary search tree. Function insertNode The steps for inserting a node in a binary search tree are as follows: If *treePtr is NULL, create a new node. Call malloc, assign the allocated memory to *treePtr Assign to (*treePtr)->data the integer to be stored Assign to (*treePtr)->leftPtr and (*treePtr)->rightPtr the value NULL Return control to the caller (either main or a previous call to insertNode) Trees If *treePtr is not NULL and the value to be inserted is less than (*treePtr)->data, function insertNode is called with the address of (*treePtr)->leftPtr to insert the node in the left subtree of the node pointed to by treePtr. If the value to be inserted is greater than (*treePtr)->data, insertNode is called with the address of (*treePtr)->rightPtr to insert the node in the right subtree of the node pointed to by treePtr Otherwise, the recursive steps continue until a NULL pointer is found, then Step 1 is executed to insert the new node. Traversals: Functions inOrder, preOrder and postOrder Functions inOrder, preOrder and postOrder each receive a tree (i.e., the pointer to the root node of the tree) and traverse the tree. The steps for an inOrder traversal are: Traverse the left subtree inOrder. Process the value in the node. Traverse the right subtree inOrder. The value in a node is not processed until the values in its left subtree are processed. The inOrder traversal of the tree in figure is: 27 6 13 17 27 33 42 48 13 42 6 17 33 48 Traversals: Functions inOrder, preOrder and postOrder The inOrder traversal of a binary search tree prints the node values in ascending order. The process of creating a binary search tree actually sorts the data— and thus this process is called the binary tree sort. Traversals: Functions inOrder, preOrder and postOrder The steps for a preOrder traversal are: Process the value in the node. Traverse the left subtree preOrder. Traverse the right subtree preOrder. The value in each node is processed as the node is visited. After the value in a given node is processed, the values in the left subtree are processed, then those in the right subtree are processed. Traversals: Functions inOrder, preOrder and postOrder The preOrder traversal of the tree in figure is: 27 13 6 17 42 33 48 The steps for a postOrder traversal are: Traverse the left subtree postOrder. Traverse the right subtree postOrder. Process the value in the node. The value in each node is not printed until the values of its children are printed. The postOrder traversal of the tree in the figure is: 6 17 13 33 48 42 27 27 13 42 6 17 33 48 Trees Upcoming code creates a binary search tree and traverses it three ways— inorder, preorder and postorder. The program generates 10 random numbers and inserts each in the tree, except that duplicate values are discarded. 103 104 105 © 2016 Pearson Education, Ltd. All rights reserved. Duplicate Elimination The binary search tree facilitates duplicate elimination. As the tree is being created, an attempt to insert a duplicate value will be recognized because a duplicate will follow the same “go left” or “go right” decisions on each comparison as the original value did. Thus, the duplicate will eventually be compared with a node in the tree containing the same value. The duplicate value may simply be discarded at this point. Binary Tree Search Searching a binary tree for a value that matches a key value is also fast. If the tree is tightly packed, each level contains about twice as many elements as the previous level. So a binary search tree with n elements would have a maximum of log2n levels, and thus a maximum of log2n comparisons would have to be made either to find a match or to determine that no match exists. This means, for example, that when searching a (tightly packed) 1,000,000-element binary search tree, no more than 10 comparisons need to be made because 220 > 1,000,000. Other Binary Tree Operations When searching a (tightly packed) 1,000,000 element binary search tree, no more than 20 comparisons need to be made because 220 > 1,000,000. The level order traversal of a binary tree visits the nodes of the tree row-by-row starting at the root node level. On each level of the tree, the nodes are visited from left to right. References 1. Problem Solving & Program Design in C, Jeri R. Hanly & Elliot B. Koffman, Pearson 8. Edition, Global Edition 2. C How to Program, Paul Deitel, Harvey Deitel. Pearson 8th Edition, Global Edition. 3. http://www.careerride.com/C-strcpy()-and-memcpy().aspx 110