Home > front end >  Assigning value to the array of function pointers
Assigning value to the array of function pointers

Time:12-11

I am trying to have an array of arrays of function pointers, but cannot assign to it, only statically initialize it:

#define N_INPUTS 2
#define N_STATES 2

void one()
{
    //do stuff
}

void two()
{
    //do stuff
}
//etc.

typedef void (*action_map[N_INPUTS])();
action_map my_action_maps[N_STATES];


//this would work:
//action_map am1 = {one, two};
//action_map am2 = {two, one};
//action_map my_action_maps[N_STATES] = { am1, am2 };

void init()
{
    action_map am1;
    am1[0] = one;
    am1[1] = two;

    my_action_maps[0] = am1; //error "expression must be a modifiable lvalue"

    //however this works:
    my_action_maps[0][0] = one;
    my_action_maps[0][1] = two;
}

//the idea is to then handle input depending on a state with
//my_action_maps[state][input]();

I am not sure why is this happening, my_action_maps is just an array of pointers to function pointers, isn't it? Why it can initialized with initializer but then it is not modifiable?

CodePudding user response:

This isn't really about function pointers, they are just making it harder to see the issue.

The type action_map is an array of function pointers. am1 and my_action_maps[0] are both of that type. But in C, you cannot assign an array to another array. It's the same issue as this:

int a[3] = {1,2,3};
int b[3] = {4,5,6};
a = b;             // error

Current versions of gcc and clang both give a more useful message that explicitly says the problem is assigning to an array type. You might consider switching or upgrading your compiler.

You need to copy the elements one by one with a loop, or with memcpy. Thanks to array-pointer decay, you could do:

memcpy(my_action_maps[0], am1, sizeof(action_map));

Alternatively, you could wrap your action_map type in a struct, since you can assign structs to one another. But then it's a little more awkward to access the members.

typedef struct {
    void (*actions[2])();
} action_map;
action_map my_action_maps[2];

void init(void) {
    action_map am1;
    am1.actions[0] = one;
    am1.actions[1] = two;
    // or: action_map am1 = { { one, two } };
    // or: action_map am1 = { one, two };
    my_action_maps[0] = am1; // ok
}

By the way, regarding your function and type declarations using empty parentheses, I suggest reading func() vs func(void) in C99 and Is it better to use C void arguments "void foo(void)" or not "void foo()"?. It may look like typedef void (*action_map[N_INPUTS])(); declares an array of pointers to functions taking no arguments, but it actually declares an array of pointers to functions taking unspecified arguments. This is supported mostly for compatibility with old versions of C and should generally not be used in new programs.

If you do am1[0](1,2,3); you will not get a compile error, and the compiler will happily attempt to pass three arguments to the function one that is not supposed to take any. This is undefined behavior. On some platforms, they might just be ignored, but you won't be alerted that you probably meant something else. On other platforms this may crash or worse. For instance, on a system using a calling convention where the called function is supposed to pop the stack (like the stdcall convention on Windows 32-bit compilers), calling a function with the wrong number of arguments will corrupt the stack.

So a better choice would be

typedef void (*action_map[N_INPUTS])(void);

Then action_map am1; am1[0](1,2,3); will cause a compiler error.

For consistency, I think it is also best to use void when defining a function with no parameters, e.g. void one(void) { ... }.

  • Related