views:

5096

answers:

6

I need to declare an array of pointers to functions like so:

extern void function1(void);
extern void function2(void);
...

void (*MESSAGE_HANDLERS[])(void) = {
   function1,
   function2,
   ...
};

However, I want the the array to be declared as constant -- both the data in the array and the pointer to the data. Unfortunately, I do not recall where to place the const key-word(s).

I'm assuming the actual pointer, MESSAGE_HANDLERS in this case, is already constant because it is declared as an array. On the otherhand, couldn't the function pointers within the array be change at runtime if it is declared as shown?

+7  A: 

In situations like this, do a typedef to name your function pointer, that makes it far simpler.

typedef void (*MESSAGE_HANDLER)(void);

with that in place, it should be just:

const MESSAGE_HANDLER handlers[] = { function1, function2 };

To get the actual content of the array constant.

unwind
+1  A: 

With VisualStudio 2008, I get:

void (* const MESSAGE_HANDLERS[])(void) = {
   NULL,
   NULL
};

int main ()
{
    /* Gives error 
        '=' : left operand must be l-value
    */
    MESSAGE_HANDLERS = NULL;

    /* Gives error 
        l-value specifies const object
    */
    MESSAGE_HANDLERS[0] = NULL;
}
David Norman
+6  A: 

cdecl says:

cdecl> explain void (* const foo[])(void)
declare foo as array of const pointer to function (void) returning void

Is it what you need?

qrdl
Yes, that is exactly what I was needed. I was unaware of the cdecl (and c++decl) programs. Thanks for the tip.
Judge Maygarden
cdecl is excellent. Thanks for getting it into the SO knowledge.
David
A: 

I am not sure if this will work in 'C'. it does work in 'C++':

  • First define MESSAGE_HANDLERS as a type:

    typedef void (*MESSAGE_HANDLER)();

  • Then, use the type definition to declare your array a constant:

    MESSAGE_HANDLER const handlers[] = {function1, function2};

The trick is in the 'typedef', if you can do the same semantically in 'C', it should work too.

Curro
+3  A: 

There is a technique to remember how to build such type. First try to read pointers starting from their name and read from right to left.

How to declare that stuff without help?

Arrays

T t[5];

is an array of 5 T. To make T a function type, you write the return-type to the left, and the parameters to the right:

void t[5](void);

would be an array of 5 functions returning void and taking no parameters. But functions itself can't be stuffed in arrays! They are not objects. Only pointers to them can.

What about

void * t[5](void);

That's still wrong as it would just change the return-type to be a pointer to void. You have to use parentheses:

void (*t[5])(void);

and this will actually work. t is an array of 5 pointers to functions returning void and taking no parameters.

Great! What about an array of pointers to arras? That's very similar. The element type appears at the left, and the dimension at the right. Again, parentheses are needed because otherwise the array would become a multidimensional array of integer pointers:

int (*t[5])[3];

That's it! An array of 5 pointers to arrays of 3 int.

What about functions?

What we have just learned is true about functions too. Let's declare a function taking an int that returns a pointer to another function taking no parameter and returning void:

void (*f(int))(void);

we need parentheses again for he same reason as above. We could now call it, and call the returned function pointed to again.

f(10)();

Returning a pointer to function returning another pointer to function

What about this?

f(10)(true)(3.4);

? In other words, how would a function taking int returning a pointer to a function taking bool returning a pointer to a function taking double and returning void would look like? The answer is that you just nest them:

void (*(*f(int))(bool))(double);

You could do so endless times. Indeed, you can also return a pointer to an array just like you can a pointer to a function:

int (*(*f(int))(bool))[3];

This is a function taking int returning a pointer to a function taking bool returning a pointer to an array of 3 int

What does it have to do with const?

Now that the above explained how to build up complexer types from fundamental types, you can put const at places where you now know where they belong to. Just consider:

T c * c * c ... * c name;

The T is the basic type that we end up pointing to at the end. The c stands for either const or not const. For example

int const * const * name;

will declare name to have the type pointer to a constant pointer to a constant int. You can change name, but you cannot change *name, which would be of type

int const * const

and neither **name, which would be of type

int const

Let's apply this to a function pointer of above:

void (* const t[5])(void);

This would actually declare the array to contain constant pointers. So after creating (and initializing) the array, the pointers are const, because the const appeared after the star. Note that we cannot put a const before the star in this case, since there are no pointers to constant functions. Functions simply can't be const as that would not make sense. So the following is not valid:

void (const * t[5])(void);

Conclusion

The C++ and C way of declaring functions and arrays actually is actually a bit confusing. You have to get your head around it first, but if you understand it, you can write very compact function declarations using it.

Johannes Schaub - litb
i'm sorry mate. initially i had const in, but somehow i removed it. it's now in again, i extended it to explain it in more detail.
Johannes Schaub - litb
A: 

VERRRRY helpful. I wanted to declare a constant array of pointers to variables for initialization in a TI MSP430 embedded processor. The linker places constants in the 16K Code EEPROM, while variables get allocated in the 512 bytes of RAM space - so I really, really wanted those pointers put in ROM. The winner, and correct declaration for a constant array of constant pointers to unsigned ints is:

const unsigned int *const VarAddrs[] = {&Var1, &Var2, &Var3, &Var4,...&VarN};

Thanks muchly for providing that long-winded and detailed explanation.

It's quite a coincidence that I was writing MSP430 code when I asked this question!
Judge Maygarden