Z tej odpowiedzi na temat inżynierii oprogramowania void
traktujemy specjalnie, w zależności od tego, jak jest używana. W C
i C++
, void
służy do wskazania nieobecności danego typu danych, podczas gdy void *
jest używany do wskazywania wskaźnik, który wskazuje na jakiś danych / przestrzeni w pamięci, które nie mają typu. void *
nie można samodzielnie wyrejestrować i należy go najpierw przenieść na inny typ. Ta obsada nie musi być jawna w C
, ale musi być jawna w C++
. (Właśnie dlatego nie wylewamy wartości zwracanej przez malloc void *
).
W przypadku użycia z funkcją jako parametrem void
oznacza całkowity brak jakichkolwiek parametrów i jest jedynym dozwolonym parametrem. Próba użycia void jak typu zmiennej lub dołączenia innych argumentów powoduje błąd kompilatora:
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);
^
Podobnie niemożliwe jest zadeklarowanie zmiennej typu 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
jako wartość zwracana dla funkcji oznacza, że żadne dane nie zostaną zwrócone. Ponieważ nie można zadeklarować zmiennej typu void
, nie można uchwycić wartości zwracanej przez void
funkcję, nawet za pomocą pustego wskaźnika.
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);
^ ~~~~~~
Typless void *
to inna sprawa. Pusty wskaźnik wskazuje wskaźnik do lokalizacji w pamięci, ale nie wskazuje rodzaju danych w tym wskaźniku. (Jest to używane do osiągnięcia polimorfizmu w C , na przykład z funkcją qsort ()) . Te wskaźniki mogą być jednak trudne w użyciu, ponieważ bardzo łatwo jest przypadkowo rzucić je na niewłaściwy typ. Poniższy kod nie wrzuci żadnych błędów kompilatora C
, ale spowoduje niezdefiniowane zachowanie:
#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);
}
Poniższy kod jest jednak całkowicie legalny; rzutowanie na i z pustego wskaźnika nigdy nie zmienia wartości, którą posiada.
#include <stdio.h>
int main(void) {
double foo = 47.2;
void *bar = &foo;
double *baz = bar;
fprintf(stdout, "%f\n", *baz);
}
47,200000
Jako parametr funkcji void *
wskazuje, że typ danych przekazywanych przez wskaźnik nie jest znany i od programisty zależy, czy odpowiednio zajmie się tym, co znajduje się w tym miejscu pamięci. Jako wartość zwracana void *
oznacza, że typ zwracanych danych nie jest znany lub jest bez typu i musi być obsługiwany przez program.
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
w prototypie funkcji oznacza „brak danych” i wskazuje brak wartości zwracanej lub brak parametrów, void *
w prototypie funkcji oznacza „dane wskazane wskaźnikiem tej funkcji nie mają znanego typu” i wskazuje parametr lub wartość zwracaną którego wskaźnik musi być rzutowany na inny typ, zanim będzie można użyć danych na wskaźniku.