1

I'm a C beginner experimenting with linked lists and I found a Github repo for one. the create function is written like this:

Node* ListCreate(void* data) {
    Node *head = malloc(sizeof(Node));
    if ( head != NULL) {
        head->next = NULL;
        head->data = data;
    }
    return head;
}

I was able to use it by changing the void* to int and then in main doing:

Node *root = ListCreate(5);

But then I read a little about void pointers and it seems like they can be used as generic types sort of like C++ templates, which would be useful if I could figure out how they work. I tried several things, and the closest I've got to getting it to work is no errors, but one warning:

incompatible integer to pointer conversion passing 'int' to parameter of type 'void *'

Am I missing a step here? I first felt like I should add something to the function definition, but I'm assuming the person who wrote it knows what they're doing and I just didn't use it properly in main. So is there a different way I'm supposed to pass an argument to this function?

8
  • 1
    You might use void pointers when a single function is going to handle different types of data (and particularly when it makes sense to pass pointers to those different types of data), but when it will handle a single type, you usually try to avoid that. Note that functions like qsort() or bsearch() pass void pointers around — so if you're using those, you'll have functions that accept void pointers but convert the passed pointer to the correct (expected) type. You can't convert from integer types to pointers without explicit casts; you can't convert from floating point types at all. Commented Jul 4, 2016 at 2:47
  • 1
    I was trying to make it so my linked list's data could be set to ints, floats, or chars. Is this a lot harder to do in C than C++? Commented Jul 4, 2016 at 2:49
  • 1
    OK; what's the data type in the list? Are you storing int values, or int * values? Handling int * is easy enough in the list code, but harder in the code using the list code — you have to have a separate int variable for each pointer to point at so you can store these in the list (and there's space wasted because you have both the pointer and the value it points at). Ditto for float. When you say char, do you mean single characters (hard) or strings (aka char *, relatively easy)? Commented Jul 4, 2016 at 2:52
  • 2
    There are multiple ways to enforce homogeneity, but one is to have different list types for the different member types. You just implement them in terms of void * underneath. One issue you need to think about is 'which operations apply to your lists'. Create list, add item to list, remove item from list, destroy list — they're easy enough, and mostly non-controversial (though you need to think about who manages the data memory to avoid leaks). If you want to find values in the list, or print lists, or apply a function to each item in the list, you have to think harder. Commented Jul 4, 2016 at 3:10
  • 2
    It's usually easier to walk before you run. :D Commented Jul 4, 2016 at 3:11

2 Answers 2

1

As is mentioned by others in comment, it is more suitable to create different list for different types.

But if you want to use the same function definition, then use can pass the pointer to your data (int or char) and cast them as void *.

/* main: start */
int main(void)
{
    Node *list_head;  /* Points to first element in list */
    Node *node_tmp;   /* Just a temporary pointer */

    int   *pint;
    char  *pchar;

    /* create an empty list */
    list_head = NULL;

    /* Note that there is no error checking done for
     * malloc, which is not good
     */

    /* Create a node which points to int */
    pint = malloc(sizeof(int));
    *pint = 10; 
    node_tmp = ListCreate((void *) pint);
    /* Add this node to list */
    list_head = add(node_tmp, list_head);

    /* Create a node which points to char */
    pchar = malloc(sizeof(char));
    *pchar = 'c';
    node_tmp = ListCreate((void *) pchar);
    /* Add this node to list */
    list_head = add(node_tmp, list_head);

    /* print total number of nodes in list */
    print_tot_nodes(list_head);
    return 0;
}

Code for add and print_tot_nodes ommited are for brevity.

Note, that functions like print_tot_nodes or add will have not much issues if data points to different data types. But if you need to implement function like Node *smallest(Node *head), which returns pointer to the node having smallest element, then it may get complicated.

So, it is easier to use different list for different types. However you can cast the actual pointer to some datatype to void * if you need to use the same function definition as seen in your original post.

Sign up to request clarification or add additional context in comments.

Comments

0

I think that there is a need to wrap for numeric literals(or cast?).
Like this:

void *BOX_VAR;//Not require if use GCC extension
#define BOX(type, value) ((*(type *)(BOX_VAR=malloc(sizeof(type))) = value), BOX_VAR)
#define UNBOX(type, value) (*(type *)(value))

Node *list = ListCreate(BOX(int, 5));
int v = UNBOX(int, list->data);
printf("%d\n", v);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.