A partir de esta respuesta sobre Ingeniería de Software, void
se trata especialmente dependiendo de cómo se use. En C
y C++
, void
se usa para indicar la ausencia de un tipo de datos, mientras que void *
se usa para indicar un puntero que apunta a algunos datos / espacio en la memoria que no tiene un tipo. void *
no se puede desreferenciar por sí solo, y primero se debe convertir a otro tipo. Este reparto no necesita ser explícito en C
, pero debe ser explícito en C++
. (Es por eso que no arrojamos el valor de retorno de malloc, que es void *
).
Cuando se usa con una función como parámetro, void
significa la ausencia total de cualquier parámetro, y es el único parámetro permitido. Intentar usar void como un tipo de variable o incluir otros argumentos da como resultado un error del compilador:
int foo(void, int); //trying to use "void" as a parameter
int bar(void baz); //trying to use "void" as an argument's type
main.c:1:8: error: 'void' must be the first and only parameter if specified
int foo(void, int);
^
main.c:2:14: error: argument may not have 'void' type
int bar(void baz);
^
Es igualmente imposible declarar una variable con tipo void
:
int main(void) {
void qux; //trying to create a variable with type void
}
main.c:5:8: error: variable has incomplete type 'void'
void qux;
void
como un valor de retorno para una función indica que no se devolverán datos. Como es imposible declarar una variable de tipo void
, es imposible capturar el valor de retorno de una void
función, incluso con un puntero nulo.
void foo(int i) { return; }
int main(void) {
void *j;
j = foo(0);
return 0;
}
main.c:5:5: error: assigning to 'void *' from
incompatible type 'void'
j = foo(0);
^ ~~~~~~
El sin tipo void *
es un caso diferente. Un puntero nulo indica un puntero a una ubicación en la memoria, pero no indica el tipo de datos en ese puntero. (Esto se usa para lograr el polimorfismo en C , como con la función qsort ()) . Sin embargo, estos punteros pueden ser difíciles de usar, ya que es muy fácil lanzarlos accidentalmente al tipo incorrecto. El siguiente código no arrojará ningún error del compilador C
, pero da como resultado un comportamiento indefinido:
#include <stdio.h>
int main(void) {
double foo = 47.2; //create a double
void *bar = &foo; //create a void pointer to that double
char *baz = bar; //create a char pointer from the void pointer, which
//is supposed to hold a double
fprintf(stdout, "%s\n", baz);
}
El siguiente código, sin embargo, es perfectamente legal; lanzar hacia y desde un puntero vacío nunca cambia el valor que tiene.
#include <stdio.h>
int main(void) {
double foo = 47.2;
void *bar = &foo;
double *baz = bar;
fprintf(stdout, "%f\n", *baz);
}
47.200000
Como parámetro de función, void *
indica que se desconoce el tipo de datos en el puntero que está pasando, y depende de usted, el programador, manejar adecuadamente lo que esté en esa ubicación de memoria. Como valor de retorno, void *
indica que el tipo de datos que se devuelve no se conoce o no tiene tipo, y el programa debe manejarlo.
int quux(void *); //a function that receives a pointer to data whose type is not known, and returns an int.
void *quuz(int); //a function that receives an int, and returns a pointer to data whose type is not known.
tl; dr void
en un prototipo de función significa "sin datos" e indica que no hay valor de retorno o ningún parámetro, void *
en un prototipo de función significa que "los datos en el puntero que se da esta función no tienen un tipo conocido" e indica un parámetro o valor de retorno cuyo puntero debe convertirse a un tipo diferente antes de que se puedan utilizar los datos en el puntero.