I am writing C code that has some dynamic arrays/list that are represented with a struct that contains the number of elements and a pointer to the elements.
I have some functions that are doing some similar work with that structs, for example printing the elements. To use the same code I decided to create a struct to that holes an abstract data type and the printing function receives a pointer to that struct.
A specific struct looks like that:
typedef struct {
int numOfElements;
int *element;
} LIST_INT, *PLIST_INT;
The generic struct look like that:
typedef struct {
int numOfElements;
void *element;
} LIST_GEN, *PLIST_GEN;
Is that the right approach?
This is the code example:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int numOfElements;
char *element;
} LIST_CHAR, *PLIST_CHAR;
typedef struct {
int numOfElements;
int *element;
} LIST_INT, *PLIST_INT;
typedef struct {
int numOfElements;
void *element;
} LIST_GEN, *PLIST_GEN;
typedef void(*fpPrintElement)(void *element);
void print_list(PLIST_GEN list, int elementSize, fpPrintElement printElement) {
int i = 0;
int num = list->numOfElements;
for (i = 0; i < num; ++i) {
printElement(((char *)(list->element) + i * elementSize));
}
return;
}
void print_int(int * element) {
printf("%d ", *element);
}
void print_char(char * element) {
printf("%c ", *element);
}
void main() {
LIST_INT list_int;
list_int.numOfElements = 10;
list_int.element = malloc(sizeof(list_int.element) * list_int.numOfElements);
for (int i = 0; i < list_int.numOfElements; ++i) {
list_int.element[i] = i * 1000;
}
LIST_INT list_char;
list_char.numOfElements = 7;
list_char.element = malloc(sizeof(list_char.element) * list_char.numOfElements);
for (int i = 0; i < list_char.numOfElements; ++i) {
list_char.element[i] = i + 35;
}
print_list((PLIST_GEN)&list_int, sizeof(*list_int.element), print_int);
print_list((PLIST_GEN)&list_char, sizeof(*list_char.element), print_char);
return;
}
2 Answers 2
Your code is valid. However note that
sizeof(list_int.element)
and
sizeof(list_chat.element)
return the size of a pointer, but it is not the de size of the type of elements into array. So, for instance, you need to write
sizeof(*list_int.element)
I don't like it that one has to pass the size of the elements to the print_list
function. Rather than print_list
I would define:
genlist_foreach(&list_int, print_int);
To make this work, the list needs to know the size of its elements beforehands, therefore I would add that as a third field into the struct.
I would further drop the LIST_INT
and LIST_CHAR
types and only keep the generic list type. Just to keep the code simple.
When the list already knows its element size, it becomes possible to define this:
int genlist_add(PLIST_GEN list, const void *pelem) {
if (list->numOfElements + 1 >= list->capacity) {
reallocate more memory and return -1 in case of error
}
void *dst = ((char *)list->element)
+ list->numOfElements * list->elementSize;
memcpy(dst, pelem, list->elementSize);
return 0;
}
You can then use the code like this:
int value = 3;
genlist_add(&list_int, &value);
value = 17;
genlist_add(&list_int, &value);
printElement
going to work as expected if you pass in a pointer to an int, a float, a different structure? It's unclear how it's going to know how big the element it's printing is. \$\endgroup\$