4

I have a linked list where each element can contain

typedef struct elem {
    struct elem *next;
    void *data;
} *elem;

Now, say I have a method like...

void blah(int random) {
    printf("testing\n");
}

And I store it inside my list...

elem lst = (elem) malloc(sizeof(elem));
lst->next = NULL;
lst->data = &blah;

How do I now call the method by taking it from my linked list?
Also, am I storing the function in my linked list properly?

4
  • void * is not a function pointer Commented Sep 13, 2014 at 1:59
  • But it stores anything, correct? That is - I can store anything inside my linked list - including a function pointer. Commented Sep 13, 2014 at 2:01
  • Ah I see. I'm gonna' learn a bit more and post another question later. :/ Commented Sep 13, 2014 at 2:03
  • 1
    I may as well point out a subtle bug outside of what the question asked. You may find yourself with strange runtime errors/segfaults if you had this as part of code of a bigger project. The way you defined elem as being a *elem means that sizeof(elem) will return the size of a pointer (dependent on the architecture of the processor/compiler directives - size could be 4 on 32bit and 8 on 64 as an example). Commented Sep 13, 2014 at 3:14

4 Answers 4

3

C standard does not allow you to cast a function pointer to void*. In order to store function pointer in a list, you need to define a field in the struct to be of the function pointer type.

One way of doing it is with a typedef:

typedef void (*fptr_int)(int); // This defines a type fptr_int

Now you can use it in your struct like this:

typedef struct elem {
    struct elem *next;
    fptr_int *function;
} *elem;

Assign it the way you did in your example:

lst->function = &blah;

Call it as if it were a function name:

lst->function(123);

The typedef does not need to be specific to the signature of the function that you want to store, though: any function pointer can be cast to another function pointer and back without an error. You can define a "generic" function pointer, like this

typedef void (*void_fptr)();

use it in your struct

typedef struct elem {
    struct elem *next;
    void_fptr *function;
} *elem;

and the use it with appropriate casts:

lst->function = (void_fptr)&blah; // Assign
...
((fptr_int)lst->function)(123);   // Call
Sign up to request clarification or add additional context in comments.

12 Comments

Actually you can cast a function pointer to void *. It is very common to do this. You can then dereference the void * with something like this ((void (*)(int))(lst->data))(42); I have done this without a typedef (although it is recommended for readability)
@Supervisor That is what they needed to do for maximal portability. Otherwise, they would have to make assumptions that pointers to data and pointers to functions have the same size, which may not be true on some architectures. For example, Harvard architecture uses different memory spaces for data and for program, and the two spaces could have different size. Without this restriction compilers would have to reserve some space inside each pointer to determine if it's a program space or a data space at runtime. This would be grossly inefficient.
@MichaelPetch You can do many things that the standard prohibits, and it will often run on your platform, or even on several platforms. The standard prohibits this, though, so the code that you posted in the comment is non-portable.
The warning occurs only if you specify "-Werror=pedantic". How appropriate ;)
@MichaelPetch That's a valid point, I edited the answer to demonstrate this alternative. Thanks!
|
3

Better:

typedef void (*blah_ptr)(int);
...
typedef struct elem {
    struct elem *next;
    blah_ptr data;
} *elem;
...
lst->data = blah;

A complete example might help:

#include <stdio.h>

typedef void (*blah_ptr)(int);

typedef struct elem {
    struct elem *next;
    blah_ptr data;
} *elem;

void blah (int random) {
  printf ("We're in blah: random=%d...\n", random);
}

void bleep (int nonrandom) {
  printf ("We're in bleep: nonrandom=%d...\n", nonrandom);
}

int
main (int argc, char *argv[]) {

  struct elem lst[2];

  lst[0].next = &lst[1];
  lst[0].data = blah;
  lst[1].next = NULL;
  lst[1].data = bleep;

  lst[0].data(10);
  lst[1].data(20);

  return 0;
}

Here is the output:

We're in blah: random=10...
We're in bleep: nonrandom=20...

1 Comment

PS: to invoke the function ptr, just call lst->data(some_number);
2

I'm not looking to answer what has already been presented by others like dasblinkenlight but I wanted to make an observation about the way the structure and type was defined and a curious malloc bug that is introduced.

You defined a type *elem. If you were to call sizeof(elem) it will return the sizeof the pointer that points to your data - not the size of the data structure itself. In your case you are allocating too few bytes via malloc and that can lead to memory corruption of the heap. I had made the observation to myself that the code was a bit hard to read as side effect. In particular this line

elem lst = (elem) malloc(sizeof(elem));

My first thought was that elem was a structure not a pointer to a structure. Usually when people create structures they do it this way:

typedef struct _elem {
    struct elem *next;
    void (*data)();
} elem;

Notice elem wasn't defined as a pointer type. Then the malloc would have looked like:

elem *lst = (elem*) malloc(sizeof(elem));

Now it is clear to me that you are dealing with pointers to elements. As well sizeof(elem) will now be the size of the elem data structure and not the size of a pointer that points to the data structure elem

The alternative if you keep your data structure as is would be to do the malloc in this fashion:

elem lst = (elem) malloc(sizeof(*lst));

lst has been defined as elem (which is a pointer). You then dereference the pointer with *lst to tell sizeof you want the size of what you are pointing at (thus size of an elem).

Comments

1
typedef void (*fptr)(int);
((fptr)lst->data)(42);

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.