La conversión de punteros es una operación común en el lenguaje C, que permite utilizar interfaces genéricas, por ejemplo, trabajar con memoria a través de void* o implementar estructuras de datos universales. Sin embargo, la conversión de tipos de punteros conlleva ciertos riesgos y está sujeta a estrictos estándares.
void* en C almacena una dirección, pero no conoce el tipo de contenido al que señala. Cualquier otro puntero (por ejemplo, int*, char*, struct mytype*) se puede convertir explícita (o implícitamente) a void* y de vuelta sin pérdida de información (si no ocurre "reducción").void*), debes asegurarte de que en esa dirección realmente haya un valor de un tipo compatible.void process(void *data) { int *arr = (int*)data; // usamos arr como un arreglo de int } int main() { double x = 10; process(&x); // PELIGROSO: convertimos double* a int*, UB }
"¿Puede cualquier puntero ser convertido de manera segura a void* y de vuelta sin pérdidas?"
Muchos responden "sí" — ya que el estándar C garantiza la conversión de cualquier puntero a objeto a void* y de vuelta sin pérdidas. Pero es importante recordar: si conviertes un puntero no objeto (por ejemplo, un puntero a función) a void* o mezclas diferentes arquitecturas (los tamaños de punteros difieren para funciones y datos), obtendrás comportamiento indefinido.
void foo() {} void *p = (void*)foo; // UB! no se puede convertir un puntero a función de esta manera
Historia
En un proyecto con un subsistema de procesamiento de datos multiplataforma, se utilizó un manejador que convertía un puntero a estructura a
void*, luego de nuevo al tipo original. Al cambiar a una arquitectura dondeint*ydouble*tenían diferentes alineaciones, intentar convertirvoid*al tipo incorrecto resultó en un "bus error" (terminación anormal).
Historia
En un proyecto embebido, un programador implementó un búfer circular con una interfaz universal en
void*, pero olvidó los estrictos requisitos de alineación (asignó memoria para un arreglo dechar, pasándolo comoint*). En algunas plataformas, los datos se volvieron "incomprensibles para el hardware" — surgieron errores de lectura y un funcionamiento inestable.
Historia
En una colección dinámica se utilizó el almacenamiento de direcciones como
void*, pero se olvidó que un puntero a función no se puede convertir avoid*. Intentar almacenar y pasar un manejador de eventos (callback) a través de este campo resultó en fallos solo en algunas plataformas, y localizar el error fue extremadamente difícil.