Задать вопрос
@sddvxd

Зачем нужен указатель на void?

Здравствуйте! Очень интересует вопрос - зачем он все-таки нужен. Вернее зачем он нужен я знаю - допустим нам нужно выделить блок из 100 байт. Подходящего типа для такого размера нету - поэтому используем void *. Но одного я не понял - как компилятор "вычленяет" эти 100 байт если, я, допустим, передал участок памяти функции? Зачем тогда вообще придумали давать типы указателям, почему бы для всех не использовать void *?

//header.h
void shower(PVOID pv){???}
//realize.cpp
PVOID pv = malloc(100);
shower(pv);
  • Вопрос задан
  • 2770 просмотров
Подписаться 1 Простой Комментировать
Решения вопроса 1
@Mercury13
Программист на «си с крестами» и не только
void* используется как указатель на сырые байтовые данные, не имеющие конкретного типа.
Обычно это используется…
1. В чтении-записи в файлы и на устройства, когда мы можем писать туда абсолютно любые типы.
2. В «многоликих» функциях, которые могут принимать данные разных типов (malloc/calloc, часть функций WinAPI и ODBC).
3. Как дескриптор — указатель, который запрещается разыменовывать. В Си для этого также часто используют указатель на недоопределённый тип, в Паскале с другими правилами эквивалентности типов — на пустой record. Но только пока не появится очередная многоликая функция вроде CloseHandle.
4. Для обеспечения т.н. замыкания — передачи callback’у контекста, откуда была вызвана функция, вызвавшая callback.
BOOL WINAPI EnumWindows(
  _In_ WNDENUMPROC lpEnumFunc,
  _In_ LPARAM      lParam
);

BOOL CALLBACK EnumWindowsProc(
  _In_ HWND   hwnd,
  _In_ LPARAM lParam
);

Вот этот LPARAM, который обычно определяется как какой-то указатель, и есть замыкание. Функция EnumWindows обещает передать его в функцию lpEnumFunc без изменений.
(В Си++ для этого также используют виртуальные интерфейсы, но такой метод, сами понимаете, языкозависим и не годится для межъязыкового API.)

Что происходит на стороне функции? Одно из двух (считаем, функция написана на ЯВУ).
1. Либо вызывается некая функция устройства, которая говорит: «записать 100 байт», и дальше уже работает железо.
2. Либо мы преобразуем void* в нужный нам тип и работаем с ним.

Типы указателям дают по трём причинам.
1. Вы забыли про операцию «разыменовать указатель». Чтобы его разыменовать, он должен иметь тип!
2. Чтобы не ошибаться и не переприсвоить несовместимые указатели.
3. Для полиморфизма — в Си++, давая delete x, мы даже можем не хранить, сколько байтов в блоке, поскольку мы знаем длину типа. (Есть ещё и виртуальные классы, но это другой вопрос.)
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы