void *

Очень часто в языке C будут встречаться неопределенные указатели типа void . Как следуем из их названия они указывают на то "не знаю что". Это так сказать указатели на будущее. Когда еще не решено на какой из объектов указывать (правда тип объекта однотипный должен быть) .

Очень удобно использовать указатели void как указатели на функции.

Почему это происходит?

Все банально - это стремление к универсальности (многократного использования одного и того же кода). Например : HAL Cube MX (для контроллеров STM32) генерирует один и тот же код для разных вариантов USB устройств MSD, CDC, HID и т.д. То есть грубо говоря основная начальная часть кода у всех одинаковая , а вот потом надо как-то учитывать особенности (у каждого устройства свои) и тогда приходит идея сделать один указатель типа void * и в каждом конкретном случае он будет указывать на конкретную реализацию конкретного устройства usb. Так наверное и придумали void *pData.

Основное неудобство дальнейшего использования такого void * то , что всегда потом надо помечать компилятору на какой тип данных он указывает. Ну например :

PCD_HandleTypeDef hpcd = (PCD_HandleTypeDef*) pdev->pData .

Иначе никак нельзя так как компилятор не поймет на что указывает pData ведь он объявлен как void *. То есть надо вставлять перфикс (PCD_HandleTypeDef*).

Ну как следствие компилятору наплевать на самом деле инициализировали вы pData , указывает он на что-то вообще или нет. Вся ответственность лежит на вас господа программисты (впрочем как и всегда).

Еще огромное неудобство в поиске void * когда они используются как указатели на функции. С их помощью можно универсализировать стандартный подход к разработке общего кода многими программистами, но очень неудобно потом в проекте искать какая функция используется при под конкретно этим указателем, так как среда разработки не может сразу подсказать , так как это определяется динамически уже на этапе выполнения программы.