¿Qué significa void * y cómo usarlo?

Hoy cuando estaba leyendo el código de otros, vi algo como void *func(void* i); , ¿qué significa este void* aquí para el nombre de la función y para el tipo de variable, respectivamente?

Además, ¿cuándo necesitamos usar este tipo de puntero y cómo usarlo?

Un puntero a void es un tipo de puntero “genérico”. Un void * se puede convertir a cualquier otro tipo de puntero sin un molde explícito. No puede desreferenciar un void * o hacer aritmética de puntero con él; primero debe convertirlo en un puntero a un tipo de datos completo.

Se usa en lugares donde necesita poder trabajar con diferentes tipos de punteros en el mismo código. Un ejemplo comúnmente citado es la función de biblioteca qsort :

 void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); 

base es la dirección de una matriz, nmemb es la cantidad de elementos en la matriz, size es el tamaño de cada elemento y compar es un puntero a una función que compara dos elementos de la matriz. Se llama así:

 int iArr[10]; double dArr[30]; long lArr[50]; ... qsort(iArr, sizeof iArr/sizeof iArr[0], sizeof iArr[0], compareInt); qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareDouble); qsort(lArr, sizeof lArr/sizeof lArr[0], sizeof lArr[0], compareLong); 

Las expresiones de matriz iArr , dArr y lArr se convierten implícitamente de tipos de matriz a tipos de puntero en la llamada a función, y cada una se convierte implícitamente de “puntero a int / double / long ” a “puntero a void “.

Las funciones de comparación se verían así:

 int compareInt(const void *lhs, const void *rhs) { const int *x = lhs; // convert void * to int * by assignment const int *y = rhs; if (*x > *y) return 1; if (*x == *y) return 0; return -1; } 

Al aceptar void * , qsort puede funcionar con matrices de cualquier tipo.

La desventaja de utilizar void * es que arrojas seguridad tipo por la ventana y hacia el tráfico que se aproxima. No hay nada que lo proteja de usar la rutina de comparación incorrecta:

 qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareInt); 

compareInt espera que sus argumentos apunten a int s, pero en realidad está trabajando con double s. No hay forma de detectar este problema en tiempo de comstackción; terminará con una matriz mal ordenada.

Usar un vacío * significa que la función puede tomar un puntero que no necesita ser de un tipo específico. Por ejemplo, en las funciones de socket, tiene

 send(void * pData, int nLength) 

esto significa que puede llamarlo de muchas maneras, por ejemplo

 char * data = "blah"; send(data, strlen(data)); POINT p; px = 1; py = 2; send(&p, sizeof(POINT)); 
 void* 

es un ‘puntero a la memoria sin suposiciones de qué tipo hay almacenado’. Puede usar, por ejemplo, si desea pasar un argumento a la función y este argumento puede ser de varios tipos y en la función manejará cada tipo.

Puede echar un vistazo a este artículo sobre punteros http://www.cplusplus.com/doc/tutorial/pointers/ y leer el capítulo: punteros vacíos .

Esto también funciona para el lenguaje C.

El tipo de puntero nulo es un tipo especial de puntero. En C ++, void representa la ausencia de tipo, por lo que los punteros vacíos son punteros que apuntan a un valor que no tiene ningún tipo (y por lo tanto también una longitud indeterminada y propiedades de desreferencia indeterminadas).

Esto permite que los punteros vacíos apunten a cualquier tipo de datos, desde un valor entero o un flotador hasta una cadena de caracteres. Pero a cambio tienen una gran limitación: los datos señalados por ellos no se pueden desreferenciar directamente (lo cual es lógico, ya que no tenemos ningún tipo al que desreferenciar), y por esa razón siempre tendremos que convertir la dirección en el puntero vacío a algún otro tipo de puntero que apunta a un tipo de datos concreto antes de desreferenciarlo.

C es notable en este sentido. Se puede decir que el vacío es la nada vacía * es todo (puede ser todo)

Es solo este pequeño * que hace la diferencia.

Rene lo ha señalado. Un vacío * es un puntero a alguna ubicación. Lo que hay cómo “interpretar” se deja al usuario.

Es la única manera de tener tipos opacos en C. Se pueden encontrar ejemplos muy destacados, por ejemplo, en bibliotecas de estructura de datos generales o simplistas. Se trata de forma detallada en “C Interfaces e implementaciones”.

Le sugiero que lea el capítulo completo e intente comprender el concepto de un puntero para “obtenerlo”.

Un puntero de vacío se conoce como puntero genérico. Me gustaría explicar con un ejemplo de escenario pthread.

La función de hilo tendrá el prototipo como

 void *(*start_routine)(void*) 

Los diseñadores de pthread API consideraron el argumento y los valores de retorno de la función thread. Si esas cosas se hacen genéricas, podemos escribir cast para void * mientras se envía como argumento. de forma similar, el valor de retorno puede recuperarse de void * (Pero nunca utilicé los valores de retorno de la función de subprocesos).

 void *PrintHello(void *threadid) { long tid; // ***Arg sent in main is retrieved *** tid = (long)threadid; printf("Hello World! It's me, thread #%ld!\n", tid); pthread_exit(NULL); } int main (int argc, char *argv[]) { pthread_t threads[NUM_THREADS]; int rc; long t; for(t=0; t 

un void* es un puntero, pero el tipo de lo que apunta no está especificado. Cuando pase un puntero de vacío a una función, necesitará saber cuál era su tipo para devolverlo al tipo correcto más adelante en la función para usarlo. Verá ejemplos en pthreads que usan funciones con exactamente el prototipo en su ejemplo que se utilizan como la función de subprocesos. Luego puede usar el argumento void* como un puntero a un tipo de datos genérico de su elección y luego devolverlo a ese tipo para usar dentro de su función de subprocesos. Debe tener cuidado al usar punteros vacíos, ya que a menos que recurra a un puntero de su tipo verdadero, puede terminar con todo tipo de problemas.

C11 estándar (n1570) §6.2.2.3 al1 p55 dice:

Un puntero a void se puede convertir desde o hacia un puntero a cualquier tipo de objeto. Un puntero a cualquier tipo de objeto se puede convertir en un puntero a vacío y viceversa; el resultado se comparará igual al puntero original.

Puede usar este puntero genérico para almacenar un puntero a cualquier tipo de objeto, pero no puede usar las operaciones aritméticas habituales con él y no puede deferencia.

La función toma un puntero a un tipo arbitrario y devuelve uno.

Significa puntero, puede usar este enlace para obtener más información sobre el puntero http://www.cprogramming.com/tutorial/c/lesson6.html

El VOID antes del nombre de la función significa que no devuelve nada. Solo haciendo algunas cosas. Por otro lado, VOID como parámetro lo convierte en una función genérica que puede aceptar cualquier tipo de parámetro. Pero debe proporcionar la función con el tamaño del parámetro this.