As stated in the tags, I'm a beginner to C. This code implements a dynamic array (List
in .NET, vector
in C++, etc) for any pointer type. I would like that it be reviewed on memory usage and code structure/style.
list.h
#ifndef _game_structures_list_h
#define _game_structures_list_h
#include <stdbool.h>
typedef enum {
LIST_ERR_NONE = 0x00,
LIST_ERR_POP_EMPTY = 0x01,
LIST_ERR_OUT_OF_BOUNDS = 0x02,
LIST_ERR_MEMORY = 0x03,
LIST_ERR_UNITIALIZED = 0xff
} ListError;
typedef struct {
int capacity;
int length;
ListError error;
void **data;
} List;
/**
* Creates a new List with the given starting capacity.
* @param capacity The initial allocation size for this list, not its length!
* @return A new empty List
*/
List list_new(int capacity);
/**
* Frees memory used by the list and makes it unusable
*/
void list_destroy(List *list);
/**
* Gets the error of the last operation on this list
* @return The last error, or LIST_ERR_NONE if successful
*/
ListError list_error(List *list);
/**
* Gets the amount of items in the list
* @return Length of list
*/
int list_length(List *list);
/**
* Removes all elements from the list
* @param list The list to be cleared
*/
void list_clear(List *list);
/**
* Access the item at the given index.
* @return Pointer to the item at the given index
*/
void *list_get(List *list, int index);
/**
* Sets the value at the given index to the item given
*/
void list_set(List *list, void *item, int index);
/**
* Adds an item to the end of the list
* @param list Where to add the item to
* @param item The value to be added
*/
void list_push(List *list, void *item);
/**
* Removes the last item from the list and returns it
* @param list The list to be popped
* @return The value removed
*/
void *list_pop(List *list);
/**
* Adds a new item to a list at the given position, moving all remaining items to the right
* @param list The list to which the item will be added
* @param item The value to be added
* @param index The position in the list where the item will be placed
*/
void list_insert(List *list, void *item, int index);
/**
* Removes the item at the given index from the list, returning it and shifting all remaining items to fill the gap
* @param list The list to remove
* @param index Index of the item to be removed
* @return The removed value
*/
void *list_remove(List *list, int index);
#endif
list.c
#include <stdlib.h>
#include <string.h>
#include "list.h"
#define L_ERR(list, err) list->error = err; return
#define L_OK(list) L_ERR(list, LIST_ERR_NONE)
#define L_BOUNDS(list, index, ...) if (index < 0 || index >= list->length) {L_ERR(list, LIST_ERR_OUT_OF_BOUNDS) __VA_ARGS__;}
#define CAPACITY_FACTOR 2
static inline bool change_capacity(List *list, int new_capacity) {
void **new_data = realloc(list->data, new_capacity * sizeof(*list->data));
if (new_data) {
list->data = new_data;
list->capacity = new_capacity;
L_OK(list) true;
}
L_ERR(list, LIST_ERR_MEMORY) false;
}
static inline bool adjust_capacity(List *list) {
int length = list->length;
int capacity = list->capacity;
int half_cap = capacity / CAPACITY_FACTOR;
if (length < half_cap) {
return change_capacity(list, half_cap);
} else if (length == capacity) {
return change_capacity(list, capacity * CAPACITY_FACTOR);
}
L_OK(list) true;
}
List list_new(int capacity) {
List list = {
.capacity = 0,
.length = 0,
.data = NULL
};
if (capacity) {
change_capacity(&list, capacity);
}
return list;
}
void list_destroy(List *list) {
list_clear(list);
free(list->data);
list->data = NULL;
list->capacity = 0;
}
ListError list_error(List *list) {
if (!list->capacity) {
list->error = LIST_ERR_UNITIALIZED;
}
return list->error;
}
int list_length(List *list) {
return list->length;
}
void list_clear(List *list) {
list->length = 0;
L_OK(list);
}
void *list_get(List *list, int index) {
L_BOUNDS(list, index, NULL);
L_OK(list) list->data[index];
}
void list_set(List *list, void *item, int index) {
L_BOUNDS(list, index, );
list->data[index] = item;
L_OK(list);
}
void list_push(List *list, void *item) {
if (!adjust_capacity(list)) return;
list->data[list->length] = item;
list->length++;
L_OK(list);
}
void *list_pop(List *list) {
if (!list->length) {
L_ERR(list, LIST_ERR_OUT_OF_BOUNDS) NULL;
}
void *item = list->data[list->length - 1];
if (!adjust_capacity(list)) return NULL;
list->length--;
L_OK(list) item;
}
void list_insert(List *list, void *item, int index) {
L_BOUNDS(list, index, );
if (!adjust_capacity(list)) return;
void **base = list->data + index;
int size = (list->length - index) * sizeof(*list->data);
if (!memmove(base + 1, base, size)) {
L_ERR(list, LIST_ERR_MEMORY);
}
list->data[index] = item;
list->length++;
L_OK(list);
}
void *list_remove(List *list, int index) {
L_BOUNDS(list, index, NULL);
if (!adjust_capacity(list)) return NULL;
void **base = list->data + index;
void *item = *base;
int size = (list->length - index) * sizeof(*list->data);
if (!memmove(base, base + 1, size)) {
L_ERR(list, LIST_ERR_MEMORY) NULL;
}
list->length--;
return item;
}