Подтвердить что ты не робот

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Массивы

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 в те места, где теперь вы знаете, где они принадлежат. Просто подумайте:

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 способ объявления функций и массивов на самом деле на самом деле немного запутан. Сначала вы должны обдумать это, но если вы это понимаете, вы можете написать очень компактные декларации функций, используя его.

Ответ 2

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

typedef void MESSAGE_HANDLER(void);

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

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

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

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

Ответ 3

cdecl говорит:

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

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

Ответ 4

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

Ответ 5

Я не уверен, будет ли это работать в 'C'. он работает в 'С++':

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

    typedef void (*MESSAGE_HANDLER)();

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

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

Трюк находится в typedef, если вы можете сделать то же семантически в 'C', он тоже должен работать.