Rename year directories to allow natural ordering
This commit is contained in:
Binary file not shown.
@ -0,0 +1,15 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int my_int;
|
||||
int* my_int_pointer;
|
||||
long my_long;
|
||||
double * my_double_pointer;
|
||||
char ** my_char_pointer_pointer;
|
||||
|
||||
printf("The size of my_int is %lu bytes\n", sizeof(my_int));
|
||||
printf("The size of my_int_pointer is %lu bytes\n", sizeof(my_int_pointer));
|
||||
printf("The size of my_long is %lu bytes\n", sizeof(my_long));
|
||||
printf("The size of my_double_pointer is %lu bytes\n", sizeof(my_double_pointer));
|
||||
printf("The size of my_char_pointer_pointer is %lu bytes\n", sizeof(my_char_pointer_pointer));
|
||||
}
|
8
year3/semester1/CT331: Programming Paradigms/assignments/assignment1/code/question2/.idea/.gitignore
generated
vendored
Normal file
8
year3/semester1/CT331: Programming Paradigms/assignments/assignment1/code/question2/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/question2.iml" filepath="$PROJECT_DIR$/.idea/question2.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
#include "linkedList.h"
|
||||
#include "tests.h"
|
||||
|
||||
int main(int argc, char* argv[]){
|
||||
runTests();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "linkedList.h"
|
||||
|
||||
typedef struct listElementStruct{
|
||||
char* data;
|
||||
size_t size;
|
||||
struct listElementStruct* next;
|
||||
} listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(char* data, size_t size){
|
||||
listElement* e = malloc(sizeof(listElement));
|
||||
if(e == NULL){
|
||||
//malloc has had an error
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
char* dataPointer = malloc(sizeof(char)*size);
|
||||
if(dataPointer == NULL){
|
||||
//malloc has had an error
|
||||
free(e); //release the previously allocated memory
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
strcpy(dataPointer, data);
|
||||
e->data = dataPointer;
|
||||
e->size = size;
|
||||
e->next = NULL;
|
||||
return e;
|
||||
}
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start){
|
||||
listElement* current = start;
|
||||
while(current != NULL){
|
||||
printf("%s\n", current->data);
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* el, char* data, size_t size){
|
||||
listElement* newEl = createEl(data, size);
|
||||
listElement* next = el->next;
|
||||
newEl->next = next;
|
||||
el->next = newEl;
|
||||
return newEl;
|
||||
}
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after){
|
||||
listElement* delete = after->next;
|
||||
listElement* newNext = delete->next;
|
||||
after->next = newNext;
|
||||
//need to free the memory because we used malloc
|
||||
free(delete->data);
|
||||
free(delete);
|
||||
}
|
||||
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list) {
|
||||
int length = 0;
|
||||
listElement* current = list;
|
||||
|
||||
// traversing the list and counting each element
|
||||
while(current != NULL){
|
||||
length++;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, char* data, size_t size) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
// assuming that the desired return value here is the popped element, as is standard for POP operations
|
||||
listElement* pop(listElement** list) {
|
||||
// don't bother if list is non existent
|
||||
if (*list == NULL) { return NULL; }
|
||||
;
|
||||
// getting reference to the element to be popped
|
||||
listElement* poppedElement = *list;
|
||||
|
||||
// make the the second element the new head of the list -- this could be NULL, so the list would be NULL also
|
||||
*list = (*list)->next;
|
||||
|
||||
// detach the popped element from the list
|
||||
poppedElement->next = NULL;
|
||||
|
||||
return poppedElement;
|
||||
}
|
||||
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
// essentially the same as push
|
||||
void enqueue(listElement** list, char* data, size_t size) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// dequeue an element from the tail of the list by removing the element from the list via side effects, and returning the removed item
|
||||
// assuming that we want to return the dequeued element rather than the list itself, as enqueue returns nothing and uses side effects, so dequeue should also use side effects
|
||||
listElement* dequeue(listElement* list) {
|
||||
// there are three cases that we must consider: a list with 0 elements, a list with 1 element, & a list with >=2 elements
|
||||
|
||||
// don't bother if list is non existent
|
||||
if (list == NULL) { return NULL; }
|
||||
|
||||
// if there is only one element in the list, i.e. the head element is also the tail element, just returning this element
|
||||
// this means that the listElement pointer that was passed to this function won't be updated
|
||||
// ideally, we would set it to NULL but we can't do that since `list` is a pointer that has been passed by value, so we can't update the pointer itself. we would need a pointer to a pointer to have been passed
|
||||
if (list->next == NULL) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// traversing the list to find the second-to-last element
|
||||
listElement* current = list;
|
||||
while (current->next->next != NULL) {
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// get reference to the element to be dequeued
|
||||
listElement* dequeuedElement = current->next;
|
||||
|
||||
// make the penultimate element the tail by removing reference to the old tail
|
||||
current->next = NULL;
|
||||
|
||||
return list;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
#ifndef CT331_ASSIGNMENT_LINKED_LIST
|
||||
#define CT331_ASSIGNMENT_LINKED_LIST
|
||||
|
||||
typedef struct listElementStruct listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(char* data, size_t size);
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start);
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* after, char* data, size_t size);
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after);
|
||||
|
||||
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list);
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, char* data, size_t size);
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
listElement* pop(listElement** list);
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
void enqueue(listElement** list, char* data, size_t size);
|
||||
|
||||
// dequeue an element from the tail of the list
|
||||
listElement* dequeue(listElement* list);
|
||||
|
||||
#endif
|
Binary file not shown.
@ -0,0 +1,57 @@
|
||||
#include <stdio.h>
|
||||
#include "tests.h"
|
||||
#include "linkedList.h"
|
||||
|
||||
void runTests(){
|
||||
printf("Tests running...\n");
|
||||
|
||||
listElement* l = createEl("Test String (1).", sizeof("Test String (1)."));
|
||||
//printf("%s\n%p\n", l->data, l->next);
|
||||
//Test create and traverse
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
//Test insert after
|
||||
printf("Testing insertAfter()\n");
|
||||
listElement* l2 = insertAfter(l, "another string (2)", sizeof("another test string(2)"));
|
||||
insertAfter(l2, "a final string (3)", sizeof("a final string(3)"));
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test length function
|
||||
printf("Testing length()\n");
|
||||
int l_length = length(l);
|
||||
printf("The length of l is %d\n\n", l_length);
|
||||
|
||||
// test push
|
||||
printf("Testing push()\n");
|
||||
push(&l, "yet another test string", sizeof("yet another test string"));
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// test pop
|
||||
printf("Testing pop()\n");
|
||||
listElement* popped = pop(&l);
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// Test delete after
|
||||
printf("Testing deleteAfter()\n");
|
||||
deleteAfter(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test enqueue
|
||||
printf("Testing enqueue()\n");
|
||||
enqueue(&l, "enqueued test string", sizeof("enqueued test string"));
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test dequeue
|
||||
printf("Testing dequeue()\n");
|
||||
dequeue(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
printf("\nTests complete.\n");
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
#ifndef CT331_ASSIGNMENT_TESTS
|
||||
#define CT331_ASSIGNMENT_TESTS
|
||||
|
||||
void runTests();
|
||||
|
||||
#endif
|
Binary file not shown.
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
#include "genericLinkedList.h"
|
||||
#include "tests.h"
|
||||
|
||||
int main(int argc, char* argv[]){
|
||||
runTests();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,167 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "genericLinkedList.h"
|
||||
|
||||
typedef struct listElementStruct{
|
||||
void* data;
|
||||
void (*printFunction)(void*);
|
||||
size_t size;
|
||||
struct listElementStruct* next;
|
||||
} listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(void* data, size_t size, void (*printFunction)(void*)) {
|
||||
listElement* e = malloc(sizeof(listElement));
|
||||
if(e == NULL){
|
||||
//malloc has had an error
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
void* dataPointer = malloc(sizeof(void)*size);
|
||||
if(dataPointer == NULL){
|
||||
//malloc has had an error
|
||||
free(e); //release the previously allocated memory
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
strcpy(dataPointer, data);
|
||||
e->data = dataPointer;
|
||||
|
||||
e->printFunction = printFunction;
|
||||
|
||||
e->size = size;
|
||||
e->next = NULL;
|
||||
return e;
|
||||
}
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start){
|
||||
listElement* current = start;
|
||||
while(current != NULL){
|
||||
current->printFunction(current->data);
|
||||
// printf("%s\n", current->data);
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* el, void* data, size_t size, void (*printFunction)(void*)){
|
||||
listElement* newEl = createEl(data, size, printFunction);
|
||||
listElement* next = el->next;
|
||||
newEl->next = next;
|
||||
el->next = newEl;
|
||||
return newEl;
|
||||
}
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after){
|
||||
listElement* delete = after->next;
|
||||
listElement* newNext = delete->next;
|
||||
after->next = newNext;
|
||||
//need to free the memory because we used malloc
|
||||
free(delete->data);
|
||||
free(delete);
|
||||
}
|
||||
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list) {
|
||||
int length = 0;
|
||||
listElement* current = list;
|
||||
|
||||
// traversing the list and counting each element
|
||||
while(current != NULL){
|
||||
length++;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, void* data, size_t size, void (*printFunction)(void*)) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size, printFunction);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
// assuming that the desired return value here is the popped element, as is standard for POP operations
|
||||
listElement* pop(listElement** list) {
|
||||
// don't bother if list is non existent
|
||||
if (*list == NULL) { return NULL; }
|
||||
;
|
||||
// getting reference to the element to be popped
|
||||
listElement* poppedElement = *list;
|
||||
|
||||
// make the the second element the new head of the list -- this could be NULL, so the list would be NULL also
|
||||
*list = (*list)->next;
|
||||
|
||||
// detach the popped element from the list
|
||||
poppedElement->next = NULL;
|
||||
|
||||
return poppedElement;
|
||||
}
|
||||
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
// essentially the same as push
|
||||
void enqueue(listElement** list, void* data, size_t size, void (*printFunction)(void*)) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size, printFunction);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// dequeue an element from the tail of the list by removing the element from the list via side effects, and returning the removed item
|
||||
// assuming that we want to return the dequeued element rather than the list itself, as enqueue returns nothing and uses side effects, so dequeue should also use side effects
|
||||
listElement* dequeue(listElement* list) {
|
||||
// there are three cases that we must consider: a list with 0 elements, a list with 1 element, & a list with >=2 elements
|
||||
|
||||
// don't bother if list is non existent
|
||||
if (list == NULL) { return NULL; }
|
||||
|
||||
// if there is only one element in the list, i.e. the head element is also the tail element, just returning this element
|
||||
// this means that the listElement pointer that was passed to this function won't be updated
|
||||
// ideally, we would set it to NULL but we can't do that since `list` is a pointer that has been passed by value, so we can't update the pointer itself. we would need a pointer to a pointer to have been passed
|
||||
if (list->next == NULL) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// traversing the list to find the second-to-last element
|
||||
listElement* current = list;
|
||||
while (current->next->next != NULL) {
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// get reference to the element to be dequeued
|
||||
listElement* dequeuedElement = current->next;
|
||||
|
||||
// make the penultimate element the tail by removing reference to the old tail
|
||||
current->next = NULL;
|
||||
|
||||
return list;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
#ifndef CT331_ASSIGNMENT_LINKED_LIST
|
||||
#define CT331_ASSIGNMENT_LINKED_LIST
|
||||
|
||||
typedef struct listElementStruct listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(void* data, size_t size, void (*printFunction)(void*));
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start);
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* after, void* data, size_t size, void (*printFunction)(void*));
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after);
|
||||
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list);
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, void* data, size_t size, void (*printFunction)(void*));
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
listElement* pop(listElement** list);
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
void enqueue(listElement** list, void* data, size_t size, void (*printFunction)(void*));
|
||||
|
||||
// dequeue an element from the tail of the list
|
||||
listElement* dequeue(listElement* list);
|
||||
|
||||
#endif
|
@ -0,0 +1,79 @@
|
||||
#include <stdio.h>
|
||||
#include "tests.h"
|
||||
#include "genericLinkedList.h"
|
||||
|
||||
// functions to print out different data types
|
||||
// a more professional design might be to put these in the genericLinkedList header file but i only need these for testing purposes
|
||||
void printChar(void* data) {
|
||||
printf("%c\n", *(char*) data);
|
||||
}
|
||||
|
||||
void printStr(void* data) {
|
||||
printf("%s\n", (char*) data);
|
||||
}
|
||||
|
||||
void printInt(void* data) {
|
||||
printf("%d\n", *(int*) data);
|
||||
}
|
||||
|
||||
void runTests(){
|
||||
printf("Tests running...\n");
|
||||
|
||||
listElement* l = createEl("Test String (1).", sizeof("Test String (1)."), printStr);
|
||||
//printf("%s\n%p\n", l->data, l->next);
|
||||
//Test create and traverse
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
//Test insert after
|
||||
printf("Testing insertAfter()\n");
|
||||
listElement* l2 = insertAfter(l, "another string (2)", sizeof("another string (2)"), printStr);
|
||||
insertAfter(l2, "a final string (3)", sizeof("a final string (3)"), printStr);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test length function
|
||||
printf("Testing length()\n");
|
||||
int l_length = length(l);
|
||||
printf("The length of l is %d\n\n", l_length);
|
||||
|
||||
// test push
|
||||
printf("Testing push()\n");
|
||||
push(&l, "yet another test string", sizeof("yet another test string"), printStr);
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// test pop
|
||||
printf("Testing pop()\n");
|
||||
listElement* popped = pop(&l);
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// Test delete after
|
||||
printf("Testing deleteAfter()\n");
|
||||
deleteAfter(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test enqueue
|
||||
printf("Testing enqueue()\n");
|
||||
enqueue(&l, "enqueued test string", sizeof("enqueued test string"), printStr);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test dequeue
|
||||
printf("Testing dequeue()\n");
|
||||
dequeue(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
printf("Testing pushing different data types\n");
|
||||
int myint = 42;
|
||||
push(&l, &myint, sizeof(myint), printInt);
|
||||
char mychar = 'c';
|
||||
push(&l, &mychar, sizeof(mychar), printChar);
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
printf("\nTests complete.\n");
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
#ifndef CT331_ASSIGNMENT_TESTS
|
||||
#define CT331_ASSIGNMENT_TESTS
|
||||
|
||||
void runTests();
|
||||
|
||||
#endif
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,343 @@
|
||||
%! TeX program = lualatex
|
||||
\documentclass[a4paper]{article}
|
||||
|
||||
% packages
|
||||
\usepackage{microtype} % Slightly tweak font spacing for aesthetics
|
||||
\usepackage[english]{babel} % Language hyphenation and typographical rules
|
||||
\usepackage[final, colorlinks = false, urlcolor = cyan]{hyperref}
|
||||
\usepackage{changepage} % adjust margins on the fly
|
||||
\usepackage{fontspec}
|
||||
|
||||
\usepackage{minted}
|
||||
\usepackage{xcolor}
|
||||
|
||||
\usepackage{pgfplots}
|
||||
\pgfplotsset{width=\textwidth,compat=1.9}
|
||||
|
||||
\usepackage{caption}
|
||||
\newenvironment{code}{\captionsetup{type=listing, skip=0pt}}{}
|
||||
% \captionsetup{skip=0pt}
|
||||
% \setlength{\abovecaptionskip}{3pt}
|
||||
% \setlength{\belowcaptionskip}{5pt}
|
||||
|
||||
\usepackage[yyyymmdd]{datetime}
|
||||
\renewcommand{\dateseparator}{--}
|
||||
\setmainfont{EB Garamond}
|
||||
\setmonofont[Scale=MatchLowercase]{Deja Vu Sans Mono}
|
||||
|
||||
\usepackage{titlesec}
|
||||
% \titleformat{\section}{\LARGE\bfseries}{}{}{}[\titlerule]
|
||||
% \titleformat{\subsection}{\Large\bfseries}{}{0em}{}
|
||||
% \titlespacing{\subsection}{0em}{-0.7em}{0em}
|
||||
%
|
||||
% \titleformat{\subsubsection}{\large\bfseries}{}{0em}{$\bullet$ }
|
||||
% \titlespacing{\subsubsection}{1em}{-0.7em}{0em}
|
||||
|
||||
% margins
|
||||
\addtolength{\hoffset}{-2.25cm}
|
||||
\addtolength{\textwidth}{4.5cm}
|
||||
\addtolength{\voffset}{-3.25cm}
|
||||
\addtolength{\textheight}{5cm}
|
||||
\setlength{\parskip}{0pt}
|
||||
\setlength{\parindent}{0in}
|
||||
% \setcounter{secnumdepth}{0}
|
||||
|
||||
\begin{document}
|
||||
\hrule \medskip
|
||||
\begin{minipage}{0.295\textwidth}
|
||||
\raggedright
|
||||
\footnotesize
|
||||
Name: Andrew Hayes \\
|
||||
E-mail: \href{mailto://a.hayes18@universityofgalway.ie}{\texttt{a.hayes18@universityofgalway.ie}} \hfill\\
|
||||
ID: 21321503 \hfill
|
||||
\end{minipage}
|
||||
\begin{minipage}{0.4\textwidth}
|
||||
\centering
|
||||
\vspace{0.4em}
|
||||
\Large
|
||||
\textbf{CT331} \\
|
||||
\end{minipage}
|
||||
\begin{minipage}{0.295\textwidth}
|
||||
\raggedleft
|
||||
\today
|
||||
\end{minipage}
|
||||
\medskip\hrule
|
||||
\begin{center}
|
||||
\normalsize
|
||||
Assignment 1: Procedural Programming with C
|
||||
\end{center}
|
||||
\hrule
|
||||
|
||||
\section{Question 1}
|
||||
\subsection{Part (A): Code}
|
||||
|
||||
\begin{code}
|
||||
\inputminted[texcl, mathescape, linenos, breaklines, frame=single]{C}{../code/question1/question1.c}
|
||||
\caption{\texttt{question1.c}}
|
||||
\end{code}
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=0.8\textwidth]{./images/question1.png}
|
||||
\caption{Console Output of \texttt{question1.c}}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Part (B): Comments}
|
||||
The amount of memory allocated to variables of different types in C is determined at compile-time, and is dependent on the architecture of
|
||||
the machine for which it is being compiled and the compiler used.
|
||||
\begin{itemize}
|
||||
\item On my machine, using GCC, an \verb|int| is allocated 4 bytes. This is the usual amount allocated on both 32-bit and 64-bit systems (my machine being of the
|
||||
latter kind), although older 32-bit systems used 2 bytes for an \verb|int| (the same amount as for a \verb|short|).
|
||||
4 bytes is used even on 64-bit machines to maintain backwards compatibility with older 32-bit architectures.
|
||||
\item An \verb|int*| (a pointer to a variable of type \verb|int|) is allocated 8 bytes on my machine.
|
||||
This is because that my machine has a 64-bit architecture, and therefore an address in memory is represented using 64 bits (8 bytes).
|
||||
If this were compiled for a 32-bit machine, the size of an pointer would be 4 bytes since addresses are 32-bit.
|
||||
\item A \verb|long| is allocated 8 bytes on my machine. This is because my machine is 64-bit and a \verb|long| is typically 8 bytes in length on such machines.
|
||||
On 32-bit machines, a \verb|long| is typically 4 bytes.
|
||||
\item The size of a pointer to a \verb|double| is the same as the size of any other pointer on the same machine; on 64-bit machines, pointers are 8 bytes, and on
|
||||
32-bit machines, they are 4 bytes.
|
||||
The type of data to which a pointer points has no effect on the size of the pointer, as the pointer is just a memory address.
|
||||
\item A pointer to a \verb|char| pointer is the same size as any other pointer: 8 bytes on a 64-bit machine and 4 bytes on a 32-bit machine.
|
||||
Note: it might be more intuitive to refer to a ``character pointer pointer'' as a pointer to a string in certain situations, as strings are character arrays,
|
||||
and an array variable acts as a pointer to the first element in the array.
|
||||
\end{itemize}
|
||||
|
||||
\section{Question 2}
|
||||
\begin{code}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{C}
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list);
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, char* data, size_t size);
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
listElement* pop(listElement** list);
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
void enqueue(listElement** list, char* data, size_t size);
|
||||
|
||||
// dequeue an element from the tail of the list
|
||||
listElement* dequeue(listElement* list);
|
||||
\end{minted}
|
||||
\caption{My Additions to \texttt{linkedList.h}}
|
||||
\end{code}
|
||||
|
||||
\begin{code}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{C}
|
||||
// returns the number of elements in the list
|
||||
int length(listElement* list) {
|
||||
int length = 0;
|
||||
listElement* current = list;
|
||||
|
||||
// traversing the list and counting each element
|
||||
while(current != NULL){
|
||||
length++;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// push a new element onto the head of a list and update the list reference using side effects
|
||||
void push(listElement** list, char* data, size_t size) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// pop an element from the head of a list and update the list reference using side effects
|
||||
// assuming that the desired return value here is the popped element, as is standard for POP operations
|
||||
listElement* pop(listElement** list) {
|
||||
// don't bother if list is non existent
|
||||
if (*list == NULL) { return NULL; }
|
||||
;
|
||||
// getting reference to the element to be popped
|
||||
listElement* poppedElement = *list;
|
||||
|
||||
// make the the second element the new head of the list -- this could be NULL, so the list would be NULL also
|
||||
*list = (*list)->next;
|
||||
|
||||
// detach the popped element from the list
|
||||
poppedElement->next = NULL;
|
||||
|
||||
return poppedElement;
|
||||
}
|
||||
|
||||
|
||||
// enque a new element onto the head of the list and update the list reference using side effects
|
||||
// essentially the same as push
|
||||
void enqueue(listElement** list, char* data, size_t size) {
|
||||
// create the new element
|
||||
listElement* newElement = createEl(data, size);
|
||||
|
||||
// handle malloc errors
|
||||
if (newElement == NULL) {
|
||||
fprintf(stderr, "Memory allocation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// make the the new element point to the current head of the list
|
||||
newElement->next = *list;
|
||||
|
||||
// make the list reference to point to the new head element
|
||||
*list = newElement;
|
||||
}
|
||||
|
||||
|
||||
// dequeue an element from the tail of the list by removing the element from the list via side effects, and returning the removed item
|
||||
// assuming that we want to return the dequeued element rather than the list itself, as enqueue returns nothing and uses side effects, so dequeue should also use side effects
|
||||
listElement* dequeue(listElement* list) {
|
||||
// there are three cases that we must consider: a list with 0 elements, a list with 1 element, & a list with >=2 elements
|
||||
|
||||
// don't bother if list is non existent
|
||||
if (list == NULL) { return NULL; }
|
||||
|
||||
// if there is only one element in the list, i.e. the head element is also the tail element, just returning this element
|
||||
// this means that the listElement pointer that was passed to this function won't be updated
|
||||
// ideally, we would set it to NULL but we can't do that since `list` is a pointer that has been passed by value, so we can't update the pointer itself. we would need a pointer to a pointer to have been passed
|
||||
if (list->next == NULL) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// traversing the list to find the second-to-last element
|
||||
listElement* current = list;
|
||||
while (current->next->next != NULL) {
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// get reference to the element to be dequeued
|
||||
listElement* dequeuedElement = current->next;
|
||||
|
||||
// make the penultimate element the tail by removing reference to the old tail
|
||||
current->next = NULL;
|
||||
|
||||
return list;
|
||||
}
|
||||
\end{minted}
|
||||
\caption{My Additions to \texttt{linkedList.c}}
|
||||
\end{code}
|
||||
|
||||
\begin{code}
|
||||
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{C}
|
||||
// test length function
|
||||
printf("Testing length()\n");
|
||||
int l_length = length(l);
|
||||
printf("The length of l is %d\n\n", l_length);
|
||||
|
||||
// test push
|
||||
printf("Testing push()\n");
|
||||
push(&l, "yet another test string", sizeof("yet another test string"));
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// test pop
|
||||
printf("Testing pop()\n");
|
||||
listElement* popped = pop(&l);
|
||||
traverse(l);
|
||||
printf("\n\n");
|
||||
|
||||
// Test delete after
|
||||
printf("Testing deleteAfter()\n");
|
||||
deleteAfter(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test enqueue
|
||||
printf("Testing enqueue()\n");
|
||||
enqueue(&l, "enqueued test string", sizeof("enqueued test string"));
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// test dequeue
|
||||
printf("Testing dequeue()\n");
|
||||
dequeue(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
printf("\nTests complete.\n");
|
||||
\end{minted}
|
||||
\caption{My Additions to \texttt{tests.c}}
|
||||
\end{code}
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=0.9\textwidth]{./images/question2.png}
|
||||
\caption{Console Output for Question 2}
|
||||
\end{figure}
|
||||
|
||||
\section{Question 3}
|
||||
\begin{code}
|
||||
\inputminted[linenos, breaklines, frame=single]{C}{../code/question3/genericLinkedList.h}
|
||||
\caption{\texttt{genericLinkedList.h}}
|
||||
\end{code}
|
||||
|
||||
\begin{code}
|
||||
\inputminted[linenos, breaklines, frame=single]{C}{../code/question3/genericLinkedList.c}
|
||||
\caption{\texttt{genericLinkedList.c}}
|
||||
\end{code}
|
||||
|
||||
\begin{code}
|
||||
\inputminted[linenos, breaklines, frame=single]{C}{../code/question3/tests.c}
|
||||
\caption{\texttt{tests.c}}
|
||||
\end{code}
|
||||
|
||||
\begin{figure}[H]
|
||||
\centering
|
||||
\includegraphics[width=0.9\textwidth]{./images/question3.png}
|
||||
\caption{Console Output for Question 3}
|
||||
\end{figure}
|
||||
|
||||
\section{Question 4}
|
||||
\subsection{Part 1}
|
||||
Any algorithm for traversing a singly linked list in reverse will always first require traversing the list forwards, and will therefore be \emph{at least} somewhat
|
||||
less efficient than a forwards traversal.
|
||||
One of the simplest ways to traverse a linked list in reverse is to use a recursive function.
|
||||
\begin{code}
|
||||
\begin{minted}[linenos, breaklines, frame=single]{C}
|
||||
void reverse_traverse(listElement* current){
|
||||
if (current == NULL) { return; }
|
||||
reverse_traverse(current->next);
|
||||
current->printFunction(current->data);
|
||||
}
|
||||
\end{minted}
|
||||
\caption{Recursive Function to Traverse a Singly Linked List in Reverse}
|
||||
\end{code}
|
||||
|
||||
This is quite inefficient as it requires that the function call for each node persists on the stack until the last node is reached, using a lot of stack memory.
|
||||
Another approach would be to iteratively reverse the linked list, by making some kind of data structure, linked list or otherwise, that contains the data of the
|
||||
original linked list but in reverse, and then iterating over that forwards.
|
||||
This would likely be more efficient in terms of memory \& computation.
|
||||
\\\\
|
||||
Because traversing a linked list in reverse always requires traversing it forwards first, any reverse algorithm will take at least twice as much memory \& computation
|
||||
as traversing it forwards, which is $O(n)$.
|
||||
It will also require that some way of storing the data in reverse in memory, either explicitly with a data, like in the iterative approach, or in the same manner
|
||||
as the recursive approach, wherein the data is stored in reverse by the nested structure of the function calls: as each function call returns, the call structure
|
||||
is iterated through in reverse.
|
||||
Therefore, we also have at least $O(n)$ memory usage, as we have to store some sort of reverse data structure.
|
||||
|
||||
\subsection{Part 2}
|
||||
The simplest way in which the structure of a linked list could be changed to make backwards traversal less intensive is to change it from a singly linked list to a
|
||||
doubly linked list, i.e. instead of each node in the list containing a pointer to just the next node, make each node contain a pointer to both the next node \& the
|
||||
previous node.
|
||||
The backwards traversal of a doubly linked list is no more intensive than the forwards traversal of a linked list.
|
||||
The drawback of using a doubly linked list is that it requires slightly more memory per node than a singly linked list, as you're storing an additional pointer
|
||||
for every node.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
\end{document}
|
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
@ -0,0 +1,13 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int arg, char* argc[]){
|
||||
printf("Hello assignment1.\n");
|
||||
|
||||
int my_integer;
|
||||
int* my_integer_pointer;
|
||||
long my_long;
|
||||
double *my_double_pointer;
|
||||
char **my_char_pointer_pointer;
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
#include "linkedList.h"
|
||||
#include "tests.h"
|
||||
|
||||
int main(int arg, char* argc[]){
|
||||
runTests();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "linkedList.h"
|
||||
|
||||
typedef struct listElementStruct{
|
||||
char* data;
|
||||
size_t size;
|
||||
struct listElementStruct* next;
|
||||
} listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(char* data, size_t size){
|
||||
listElement* e = malloc(sizeof(listElement));
|
||||
if(e == NULL){
|
||||
//malloc has had an error
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
char* dataPointer = malloc(sizeof(char)*size);
|
||||
if(dataPointer == NULL){
|
||||
//malloc has had an error
|
||||
free(e); //release the previously allocated memory
|
||||
return NULL; //return NULL to indicate an error.
|
||||
}
|
||||
strcpy(dataPointer, data);
|
||||
e->data = dataPointer;
|
||||
e->size = size;
|
||||
e->next = NULL;
|
||||
return e;
|
||||
}
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start){
|
||||
listElement* current = start;
|
||||
while(current != NULL){
|
||||
printf("%s\n", current->data);
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* el, char* data, size_t size){
|
||||
listElement* newEl = createEl(data, size);
|
||||
listElement* next = el->next;
|
||||
newEl->next = next;
|
||||
el->next = newEl;
|
||||
return newEl;
|
||||
}
|
||||
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after){
|
||||
listElement* delete = after->next;
|
||||
listElement* newNext = delete->next;
|
||||
after->next = newNext;
|
||||
//need to free the memory because we used malloc
|
||||
free(delete->data);
|
||||
free(delete);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
#ifndef CT331_ASSIGNMENT_LINKED_LIST
|
||||
#define CT331_ASSIGNMENT_LINKED_LIST
|
||||
|
||||
typedef struct listElementStruct listElement;
|
||||
|
||||
//Creates a new linked list element with given content of size
|
||||
//Returns a pointer to the element
|
||||
listElement* createEl(char* data, size_t size);
|
||||
|
||||
//Prints out each element in the list
|
||||
void traverse(listElement* start);
|
||||
|
||||
//Inserts a new element after the given el
|
||||
//Returns the pointer to the new element
|
||||
listElement* insertAfter(listElement* after, char* data, size_t size);
|
||||
|
||||
//Delete the element after the given el
|
||||
void deleteAfter(listElement* after);
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,25 @@
|
||||
#include <stdio.h>
|
||||
#include "tests.h"
|
||||
#include "linkedList.h"
|
||||
|
||||
void runTests(){
|
||||
printf("Tests running...\n");
|
||||
listElement* l = createEl("Test String (1).", 30);
|
||||
//printf("%s\n%p\n", l->data, l->next);
|
||||
//Test create and traverse
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
//Test insert after
|
||||
listElement* l2 = insertAfter(l, "another string (2)", 30);
|
||||
insertAfter(l2, "a final string (3)", 30);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
// Test delete after
|
||||
deleteAfter(l);
|
||||
traverse(l);
|
||||
printf("\n");
|
||||
|
||||
printf("\nTests complete.\n");
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
#ifndef CT331_ASSIGNMENT_TESTS
|
||||
#define CT331_ASSIGNMENT_TESTS
|
||||
|
||||
void runTests();
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user