A partir desta resposta em Engenharia de software, void
é tratado especialmente dependendo de como é usado. In C
e C++
, void
é usado para indicar uma ausência de um tipo de dados, enquanto que void *
é usado para indicar um ponteiro que aponta para alguns dados / espaço na memória que não possui um tipo. void *
não pode ser desreferenciado por si só e deve ser convertido para outro tipo primeiro. Essa conversão não precisa ser explícita C
, mas deve ser explícita C++
. (É por isso que não lançamos o valor de retorno do malloc, que é void *
.)
Quando usado com uma função como parâmetro, void
significa uma ausência total de quaisquer parâmetros e é o único parâmetro permitido. Tentar usar o void como um tipo de variável ou incluir outros argumentos resulta em um erro do 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);
^
É igualmente impossível declarar uma variável com o 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 um valor de retorno para uma função indica que nenhum dado será retornado. Como é impossível declarar uma variável do tipo void
, é impossível capturar o valor de retorno de uma void
função, mesmo com um ponteiro 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);
^ ~~~~~~
O tipeless void *
é um caso diferente. Um ponteiro nulo indica um ponteiro para um local na memória, mas não indica o tipo de dados nesse ponteiro. (É usado para obter polimorfismo em C , como na função qsort () .) Esses indicadores podem ser difíceis de usar, no entanto, pois é muito fácil convertê-los acidentalmente no tipo errado. O código abaixo não C
gera erros de compilação , mas resulta em um comportamento 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);
}
O código a seguir, no entanto, é perfeitamente legal; converter de e para um ponteiro nulo nunca altera o valor que ele possui.
#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 função, void *
indica que o tipo de dados no ponteiro em que você está passando não é conhecido e cabe a você, programador, lidar adequadamente com o que estiver naquele local de memória. Como valor de retorno, void *
indica que o tipo de dado retornado não é conhecido ou não possui tipo de letra e deve ser tratado pelo programa.
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
em um protótipo de função significa "sem dados" e indica nenhum valor de retorno ou sem parâmetros, void *
em um protótipo de função significa "os dados no ponteiro a que essa função é fornecida não têm um tipo conhecido" e indica um parâmetro ou valor de retorno cujo ponteiro deve ser convertido para um tipo diferente antes que os dados no ponteiro possam ser usados.