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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | #include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
// struct definitions
typedef struct _ListNode {
struct _ListNode* next;
void* a_value;
} ListNode;
typedef struct {
ListNode* head;
ListNode* tail;
int size;
} List;
// useful for testing: a struct that requires more work to destroy
// than just calling free
typedef struct {
int* array;
int length;
} IntArray;
// linked list functions
void append(List* a_list, void* a_value) {
ListNode* new_node = malloc(sizeof(*new_node));
*new_node = (ListNode) { .next = NULL, .a_value = a_value };
if (a_list->head == NULL) {
a_list->head = new_node;
} else {
a_list->tail->next = new_node;
}
a_list->tail = new_node;
a_list->size += 1;
}
void print_list(const List* a_list, void (*print_fn)(const void*)) {
for (const ListNode* node = a_list->head; node != NULL; node = node->next) {
print_fn(node->a_value);
fputc('\n', stdout);
}
}
void destroy_list(List* a_list, void (*destroy_fn)(void*)) {
ListNode* node = a_list->head;
while (node != NULL) {
ListNode* next = node->next;
// !! only call destroy_fn on a_value !!
// it is not expecting a node
destroy_fn(node->a_value);
// !! only call free on the node !!
// we cannot guarantee free is correct on a_value
free(node);
node = next;
}
}
const void* get_max_in_list(const List* a_list,
int (*compare_fn)(const void*, const void*)) {
const void* a_max = a_list->head->a_value;
for (const ListNode* node = a_list->head->next; node != NULL; node = node->next) {
// wrong, this compares the addresses
// if (node->a_value > a_max) {
if (compare_fn(node->a_value, a_max) > 0) {
a_max = node->a_value;
}
}
return a_max;
}
// functions for function addresses
char* _copy_string(const char* original) {
char* copy = malloc(sizeof(*copy) * (strlen(original) + 1));
strcpy(copy, original);
return copy;
}
// print functions
void _print_string(const void* a_data) {
const char* str = a_data;
fputs(str, stdout);
}
void _print_integer(const void* a_data) {
const int* a_num = a_data;
printf("%d", *a_num);
}
void _print_integer_in_hexadecimal(const void* a_data) {
const int* a_num = a_data;
printf("0x%x", *a_num);
}
// destroy function
void _destroy_nothing(void* a_data) {}
// compare functions
int _compare_integers(const void* a_left, const void* a_right) {
const int* a_left_num = a_left;
const int* a_right_num = a_right;
return *a_left_num - *a_right_num;
}
int _compare_integers_reversed(const void* a_left, const void* a_right) {
const int* a_left_num = a_left;
const int* a_right_num = a_right;
return *a_right_num - *a_left_num;
}
int _compare_strings(const void* a_left, const void* a_right) {
const char* left_str = a_left;
const char* right_str = a_right;
return strcmp(left_str, right_str);
}
int _compare_strings_reversed(const void* a_left, const void* a_right) {
const char* left_str = a_left;
const char* right_str = a_right;
return -strcmp(left_str, right_str);
}
int _compare_strings_by_length(const void* a_left, const void* a_right) {
const char* left_str = a_left;
const char* right_str = a_right;
return strlen(left_str) - strlen(right_str);
}
int main(int argc, char* argv[]) {
// initialization is the same, no matter the type
// we want to be consisent about which types we use later on
// number_list will store stack allocated integers
List number_list = { .head = NULL, .tail = NULL, .size = 0 };
// string_list will store heap allocated strings
List string_list = { .head = NULL, .tail = NULL, .size = 0 };
// insert elements
int numbers[] = {8, 4, 1, 10, 15, 4};
append(&number_list, numbers);
append(&number_list, numbers + 1);
append(&number_list, numbers + 2);
append(&number_list, numbers + 3);
append(&number_list, numbers + 4);
append(&number_list, numbers + 5);
// testing the integer compare function
assert(_compare_integers(numbers, numbers + 1) > 0);
assert(_compare_integers(numbers + 2, numbers + 4) < 0);
assert(_compare_integers(numbers + 1, numbers + 5) == 0);
append(&string_list, _copy_string("Grape"));
append(&string_list, _copy_string("Banana"));
append(&string_list, _copy_string("Apple"));
append(&string_list, _copy_string("Hello World"));
append(&string_list, _copy_string("Cranberry"));
append(&string_list, _copy_string("Strawberry"));
append(&string_list, _copy_string("Blueberry"));
// print lists
printf("Int list:\n");
print_list(&number_list, _print_integer);
printf("\n\nInt list in hexadecimal:\n");
print_list(&number_list, _print_integer_in_hexadecimal);
printf("\n\nString list:\n");
print_list(&string_list, _print_string);
// max elements
const int* a_max = get_max_in_list(&number_list, _compare_integers);
printf("\n\nMax number: %d\n", *a_max);
const int* a_min = get_max_in_list(&number_list, _compare_integers_reversed);
printf("Min number: %d\n", *a_min);
const char* max_str = get_max_in_list(&string_list, _compare_strings);
printf("Max string: %s\n", max_str);
const char* min_str = get_max_in_list(&string_list, _compare_strings_reversed);
printf("Min string: %s\n", min_str);
const char* longest_str = get_max_in_list(&string_list, _compare_strings_by_length);
printf("Longest string: %s\n", longest_str);
// free lists
destroy_list(&number_list, _destroy_nothing);
destroy_list(&string_list, free);
return EXIT_SUCCESS;
}
/* vim: set tabstop=4 shiftwidth=4 fileencoding=utf-8 noexpandtab: */
|
© Copyright 2024 Alexander J. Quinn & David Burnett This content is protected and may not be shared, uploaded, or distributed.