Как объявить массив постоянных указателей функций в C?

Мне нужно объявить массив указателей на такие функции:

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

Тем не менее, я хочу, чтобы массив был объявлен как постоянный – как данные в массиве, так и указатель на данные. К сожалению, я не помню, где разместить константное ключевое слово (ы).

Я предполагаю, что фактический указатель, MESSAGE_HANDLERS в этом случае, уже постоянен, потому что он объявлен как массив. С другой стороны, не могли ли переменные функции внутри массива меняться во время выполнения, если они объявлены как показано?

Существует методика запоминания того, как построить такой тип. Сначала попробуйте читать указатели, начиная с их имени и читайте справа налево.

Как объявить это без помощи?

Массивы

 T t[5]; 

представляет собой массив из 5 T. Чтобы сделать T тип функции, вы записываете возвращаемый тип влево, а параметры вправо:

 void t[5](void); 

было бы массивом из 5 функций, возвращающих пустоту и не принимающих никаких параметров . Но сами функции не могут быть заполнены массивами! Это не объекты. Только указатели на них могут.

Как насчет

 void * t[5](void); 

Это по-прежнему не так, поскольку он просто изменит тип return как указатель на void. Вы должны использовать круглые скобки:

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

и это действительно сработает. t представляет собой массив из 5 указателей на функции, возвращающие пустоту, и не принимает никаких параметров .

Большой! Как насчет массива указателей на аранжировки? Это очень похоже. Тип элемента появляется слева, а размер справа. Опять же, нужны скобки, потому что иначе массив станет многомерным массивом целых указателей:

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

Это оно! Массив из 5 указателей на массивы из 3 int .

Как насчет функций?

То, что мы только что узнали, верно и в отношении функций. Давайте объявим функцию, берущую int, которая возвращает указатель на другую функцию, не принимающую параметр и возвращающую void:

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

нам нужны скобки снова по той же причине, что и выше. Теперь мы можем вызвать его и снова вызвать возвращаемую функцию.

 f(10)(); 

Возврат указателя на функцию, возвращающую другой указатель на функцию

Как насчет этого?

 f(10)(true)(3.4); 

? Другими словами, как бы могла выглядеть функция, возвращающая int, возвращающая указатель на функцию, берущую bool, возвращающую указатель на функцию double и возвращающую пустоту ? Ответ заключается в том, что вы просто гнездите их:

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

Вы могли бы сделать это бесконечно. В самом деле, вы также можете вернуть указатель на массив так же, как указатель на функцию:

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

Это функция int, возвращающая указатель на функцию bool, возвращающую указатель на массив из 3 int

Что он должен делать с const?

Теперь, когда выше описано, как создавать сложные типы из основных типов, вы можете поместить const в те места, где вы теперь знаете, где они принадлежат. Просто подумайте:

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

T – основной тип, который мы заканчиваем тем, что указываем в конце. c означает либо const, либо не const. Например

 int const * const * name; 

будет объявлять имя для указателя типа постоянному указателю на константу int . Вы можете изменить name , но вы не можете изменить *name , которое будет иметь тип

 int const * const 

и ни **name , которое было бы типа

 int const 

Давайте применим это к указателю функции выше:

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

Это фактически объявит массив содержать постоянные указатели. Поэтому после создания (и инициализации) массива указатели являются const, потому что const появился после звезды. Обратите внимание, что мы не можем поставить const перед звездой в этом случае, так как нет указателей на постоянные функции . Функции просто не могут быть const, поскольку это не имеет смысла. Таким образом, следующее недопустимо:

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

Заключение

Способ C ++ и C декларирования функций и массивов на самом деле на самом деле немного запутан. Сначала вы должны обдумать это, но если вы это понимаете, вы можете написать очень компактные декларации функций, используя это.

cdecl говорит:

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

Это то, что вам нужно?

В подобных ситуациях введите typedef чтобы назвать свою подпись функции, что делает ее намного проще:

 typedef void MESSAGE_HANDLER(void); 

с этим на месте, это должно быть справедливо:

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

Чтобы получить фактическое содержимое константы массива.

EDIT : Удаленная часть указателя из typedef , это действительно лучше (жить и учиться).

С VisualStudio 2008 я получаю:

 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; } 

Я не уверен, что это будет работать в ‘C’. он работает в ‘C ++’:

  • Сначала определите MESSAGE_HANDLERS как тип:

    typedef void (*MESSAGE_HANDLER)();

  • Затем используйте определение типа, чтобы объявить массив постоянным:

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

Фокус в typedef , если вы можете сделать то же семантически в «C», он тоже должен работать.