0

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?

asked Dec 10, 2022 at 15:17
0

1 Answer 1

1

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) { ... }.

answered Dec 10, 2022 at 16:11
Sign up to request clarification or add additional context in comments.

1 Comment

This is really beautiful and helpful answer, also very quick. That's an epitome of how SO is supposed to work. Thank you.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.