2
\$\begingroup\$

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;
}
\$\endgroup\$
  • \$\begingroup\$ Is 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\$ – forsvarir Jan 18 '17 at 14:10
  • \$\begingroup\$ printElement is a pointer to a function that is given by the user. The user should know which elements he wants to print and there for supply the right function to do so. My main dilemma is should I use PLIST_GEN or just a VOID * as an input parameter to the abstract array \$\endgroup\$ – yoni Jan 18 '17 at 14:12
  • \$\begingroup\$ I have rolled back the last edit. Please see What should I do when someone answers my question? \$\endgroup\$ – t3chb0t Jan 22 '17 at 6:00
1
\$\begingroup\$

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)
|improve this answer|||||
\$\endgroup\$
1
\$\begingroup\$

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);
|improve this answer|||||
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.