Да, код в порядке.Здесь есть различные подводные камни и правила конвертации:
- C разбивает все типы на две основные категории: объекты и функции.Указатель на функцию - это скалярный тип , который, в свою очередь, является объектом.(C17 6.2.5)
void*
- это общий тип указателя для указателей на тип объекта.Любой указатель на тип объекта может быть неявно преобразован в / из void*
.(C17 6.3.2.3 §1). - Не существует такого общего типа указателя для указателей на тип функции.Таким образом, указатель функции не может быть преобразован в
void*
или наоборот.(C17 6.3.2.3 §1) - Однако любой тип указателя на функцию может быть преобразован в другой тип указателя на функцию и обратно, что позволяет нам использовать что-то вроде, например,
void(*)(void)
в качестве универсального типа указателя на функцию.Пока вы не вызываете функцию через неверный тип указателя на функцию, это нормально.(C17 6.3.2.3 §8)
Указатели на функции указывают на функции, но они сами по себе являются объектами, как и любой указатель.И поэтому вы можете использовать void*
для указания на адрес указателя функции .
Следовательно, использование void*
для указания на указатель на функцию подойдет.Но не использовать его, чтобы указывать непосредственно на функцию.В случае void *ptr1 = array;
массив превращается в указатель на первый элемент, a void (**)(void)
(эквивалентный voidfunc*
в вашем примере).Вы можете указать на такой указатель на указатель на функцию с помощью void*
.
Кроме того, относительно арифметики указателя:
- Арифметика указателя на
void*
невозможна.(C17 6.3.2.2) Такая арифметика является распространенным нестандартным расширением, которого следует избегать.Вместо этого используйте указатель на тип символа. - Указатель на тип символа может, как особый случай, использоваться для перебора любого объекта (C17 6.2.3.3 §7).Помимо проблем, связанных с выравниванием, это четко определено и не нарушает «строгое указание псевдонима указателя», если вы отмените ссылку на указатель символа (C17 6.5 §7).
Следовательно, (char*)ptr1 + sizeof(voidfunc);
тоже хорошо.Затем вы конвертируете из void*
в voidfunc*
, в voidfunc
, который является исходным типом указателя функции, хранящимся в массиве.
Как отмечалось в комментариях, вы можете значительно улучшить читаемость этого кода, используяtypedef
к типу функции:
typedef void (voidfunc)(void);
voidfunc* array[] = {&foo, &bar}; // Step 1
void* ptr1 = array; // Step 2
void* ptr2 = (char*)ptr1 + sizeof(voidfunc*); // Step 3
voidfunc* bar_ptr = *(voidfunc**)ptr2; // Step 4