clark cs1713 syllabus lecture notes programming assignments Dynamic (Heap) Memory C allocates dynamic memory via the void *malloc(iBytes) and void *calloc(iNum, iBytes). Unlike Java which uses garbage collection, the programmer is responsible for freeing the memory using free(void *). malloc returns a void * pointer. In this case, void doesn't mean no returned value. void * means it is returning a pointer; however, the thing it is pointing to is generic. (It can be almost anything.) You must tell it the type of thing it is referencing. This is done using type casting. See the example below. Linked List Implementations in C We can implement linked lists using dynamic memory and pointers. Instead of us keeping track of the free memory (with a free list like was done for the array implementation of a linked list), C will keep track. We are still responsible for allocating and freeing the nodes. Notice the struct Node and Node typedef. pNext points to another Node. homework set up Typically, a C run-time environment is provided two large segments of memory: run-time memory stack - manages calls(return addresses, parameters, saving registers) and automatic variables heap memory - manages dynamic memory typedef struct Node { int iInfo; struct Node *pNext; } Node; Node *pHead = NULL; Node *pNew = NULL; Node *pPrecedes = NULL; Since we want malloc to return a pointer to a Node, we type cast its results to a Node *. // to allocate a new node pNew = (Node *) malloc(sizeof(Node)); if (pNew == NULL) exitError("Memory allocation error", ""); printLL - print the iInfo for every element in the list void printLL(Node *pHead) { Node *p; printf("iInfo Values\n"); for (p = pHead; p != NULL; p = p->pNext) { printf("%7d\n", p->iInfo; } } Our iteration is similar to the array implementation. searchLL (not returning ppPrecedes) Show code for the function searchLL(Node *pHead, int iMatch) which functionally returns a pointer to the node that matches iMatch. If a matching node is not found, NULL is returned. Node *searchLL(Node *pHead, int iMatch) { Node *p; for (p = pHead; p != NULL; p = p->pNext) { if (iMatch == p->iInfo) return p; if (iMatch < p->iInfo) return NULL; } return NULL; The **pointer It means the parameter is a pointer to a pointer. This is done when we want to return a pointer through the parameter list. When we needed to return a double value like in the determineMinMax function, we declared the parameter to be a pointer to a double. If we want to return a pointer, we need a pointer to a pointer. Exercise Show code for the function searchLL(Node *pHead, int iMatch, Node **ppPrecedes) which functionally returns a pointer to the node that matches iMatch and also returns a pointer to the node that precedes that node. If a matching node is not found, NULL is returned. If there isn't a preceding node, return NULL in ppPrecedes. Node Insertion We have to allocate a node. Let's use Node *allocateNode(int iNewInfo) which returns a pointer to a new node which has been properly populated. } Recall how we return double values through the parameter ist: void determineMinMax(StudentScores *pstudentScores , double *pdMin, double *pdMax) { int i; *pdMin = 200.0; // arbitrary high value *pdMax = 0.0; for (i = 0; i < pstudentScores->iScoreCount; i++) { if (pstudentScores->dScoreM[i] < *pdMin) *pdMin = pstudentScores->dScoreM[i]; if (pstudentScores->dScoreM[i] > *pdMax) *pdMax = pstudentScores->dScoreM[i]; } } We had to pass a pointer to the doubles. To return the value, we had to dereference it. Node *searchLL(Node *pHead, int iMatch, Node **ppPrecedes) { Node *p; ?? return NULL; } Node * allocateNode(int iNewInfo) { // to allocate a new node pNew = malloc(sizeof(Node)); if (pNew == NULL) exitError("Memory allocation error", ""); pNew->iInfo = iNewInfo; pNew->pNext = NULL; return pNew; } Insert a new Node after pPrecedes. Exercise Show code for the function insertLL(Node *ppHead, int iNewInfo) which inserts the new info into the singly linked list using searchLL(). If it already exists in the list, it returns the pointer to that node. Otherwise, It returns the pointer for the new node. // to insert the new node after pPrecedes, assuming it is set // and using a singly linked list if (pPrecedes == NULL) { // insert at head ?? } else { // insert after a node ?? } Node *insertLL(Node **ppHead, int iNewInfo) { Node *pNew; Node *pPrecedes; Node *pFind; // see if it already exists ?? // Doesn't already exist. ?? return pNew; } Allocate a node and insert it