1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

/* TIPS and BEST PRACTICES for linked lists
 * ∙ Avoid duplication.
 *   - Avoid needless special cases.
 * ∙ Draw box-arrow diagrams.
 *   - Write the step number on the diagram.
 * ∙ Be careful about order.
 *   - To check yourself, try drawing box-arrow diagram if you flipped two key lines.
 * ∙ Read error message.  Ask for explanation of message if needed.
 *
 * You are encouraged to post error messages on Piazza.
 */
typedef struct _Node {
    //int value;
    void* a_value;
    struct _Node* next;
} Node;

void print_list(Node* head, void(*print_one_element_fn)(void*)) {
    // print_one_element_fn(…) is a function
    for(Node* curr = head;  curr != NULL; curr = curr -> next) {
        printf("[");
        print_one_element_fn(curr -> a_value);  // print this element
        printf("]");
        if(curr -> next != NULL) { // If curr is not the tail...
            printf("→");
        }
    }
    printf("\n");
}


//void _append(int value, Node** a_head, Node** a_tail) {
void _append(void* a_value, Node** a_head, Node** a_tail) {

    // Do not copy this code (or any other code).  Default is always no copying.

    // Allocate memory on heap for a new node.
    Node* new_node = malloc(sizeof(*new_node));
    
    // Initialize fields of the new node using a COMPOUND LITERAL.
    *new_node = (Node) { .a_value = a_value, .next = NULL };

    if(*a_head == NULL) {  // If list is empty (size=0), initialize a list of size one.
        assert(*a_tail == NULL);
        *a_head = new_node;
    }
    else {
        assert(*a_head != NULL && *a_tail != NULL);  // List not empty «before» appending new node
        (*a_tail) -> next = new_node;  // Connect tail to new node.
    }                                  // ⚠ Do not switch the order of these two steps!!!

    *a_tail = new_node;     // Set tail to new node.

    assert(*a_head != NULL && *a_tail != NULL);  // List not «after» before appending new node
    assert((*a_tail) -> a_value == a_value);
    assert((*a_tail) -> next  == NULL);  // Tail's '.next' field must always be NULL.
}

void free_list(Node** a_head, Node** a_tail) {
    // Convenience variables
    Node* head = *a_head;
    Node* tail = *a_tail;

    while(head != NULL) { // While the list is not empty
        Node* old_head = head; // Don't lose your head.
        head = head -> next;   // Second node becomes the new of the list.
        free(old_head);        // Free your mind.
    }

    // HW12: Free any heap memory that the node refers.
    
    // Set the head and tail in the caller's stack frame to NULL.
    *a_head = NULL;
    *a_tail = NULL;
}

void _print_int(void* a_n) {
    int n = *((int*)a_n);
    printf("%d", n);
}

int main(int argc, char* argv[]) {
    Node* head = NULL;
    Node* tail = head;

    int n1 = 5, n2 = 6, n3 = 7;
    _append(&n1, &head, &tail);
    _append(&n2, &head, &tail);
    _append(&n3, &head, &tail);

    print_list(head, _print_int);

    free_list(&head, &tail);
    assert(head == NULL && tail == NULL);  // List should be empty now.

    return EXIT_SUCCESS;
}
/* vim: set tabstop=4 shiftwidth=4 fileencoding=utf-8 noexpandtab: */

© Copyright 2021 Alexander J. Quinn         This content is protected and may not be shared, uploaded, or distributed.