Приведение указателей — частая операция в языке C, позволяющая использовать обобщённые интерфейсы, например, работать с памятью через void* или реализовывать универсальные структуры данных. Однако приведение типов указателей сопровождается определёнными рисками и подчиняется строгим стандартам.
void* в C хранит адрес, но не знает о типе содержимого, на который указывает. Любой другой указатель (например, int*, char*, struct mytype*) можно явно (или неявно) привести к void* и обратно без потери информации (если не происходит "сужение").void*), вы должны быть уверены, что по этому адресу действительно лежит значение совместимого типа.void process(void *data) { int *arr = (int*)data; // используем arr как массив int } int main() { double x = 10; process(&x); // ОПАСНО: приводим double* к int*, UB }
"Может ли любой указатель быть безопасно приведён к void* и обратно без потерь?"
Многие отвечают "да" — ведь стандарт C гарантирует преобразование любого объектного указателя к void* и обратно без потерь. Но важно помнить: если вы приведёте не-объектный указатель (например, указатель на функцию) к void* или смешаете разные архитектуры (размеры указателей отличаются для функций и данных), то получите неопределённое поведение.
void foo() {} void *p = (void*)foo; // UB! указатель на функцию нельзя так преобразовывать
История
В проекте с кросс-платформенной подсистемой обработки данных использовали обработчик, который приводил указатель на структуру к
void*, затем обратно к оригинальному типу. При переходе на архитектуру, гдеint*иdouble*имели разные выравнивания, попытка привестиvoid*к неверному типу привела к "bus error" (аварийному завершению).
История
В embedded-проекте программист реализовал кольцевой буфер с универсальным интерфейсом на
void*, но забыл про строгие требования к выравниванию (выделял память подcharмассив, передавал какint*). На некоторых платформах данные стали "непонятны аппаратуре" — возникали ошибка чтения и нестабильная работа.
История
В динамической коллекции использовали хранение адресов как
void*, но забыли, что указатель на функцию нельзя приводить кvoid*. Попытка хранить и передавать обработчик событий (callback) через такое поле привела к сбоям только на части платформ, причем отловить ошибку было крайне сложно.