foo (void) vs foo (void *)

9

Pod względem funkcjonalnym i składniowym, czy istnieje różnica między funkcją, której prototypem jest int foo(void)i int foo(void *)?

Znam różnicę między, na przykład, int bar(int)a int bar(int *)- jeden z nich szuka int, a drugi szuka wskaźnika int. Czy voidzachowuje się w ten sam sposób?

Nick Reed
źródło
Odpowiedź na powiązane pytanie: stackoverflow.com/a/1043209/434551 .
R Sahu,
Bardziej interesująca może być różnica między foo(void)i foo().
Maxim Egorushkin,

Odpowiedzi:

10

Z tej odpowiedzi na temat inżynierii oprogramowania void traktujemy specjalnie, w zależności od tego, jak jest używana. W Ci C++, voidsł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 voidoznacza 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;

voidjako 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 voidfunkcję, 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.

Nick Reed
źródło
void * ... must be cast to another type first, but may be done so without an explicit cast.Nieprawda w C ++. W C ++ formularz konwersji void*musi być jawny. PS nazywając rzutowanie jawne jest zbędne, ponieważ rzutowanie jest z definicji jawną konwersją.
eerorika
Zaktualizowano, aby odzwierciedlić różnice w C / C ++, dziękuję za informację!
Nick Reed,
4

foo(void) - funkcja bez parametrów

foo(void *)- funkcja z jednym void *parametrem

Co to jest void *? Jest to tylko wskaźnik do danych bez określonego typu. Można go rzutować na dowolny inny typ wskaźnika

unsigned add(void *arr)
{
   unsigned *uarr = arr;
   return uarr[0] + uarr[1];
}
P__J__
źródło
Niezbędna odpowiedź, więc jest najlepsza. Chciałbym tylko zastanowić się, jak to jest wyjątek we (type) vs. (type *)wszechświecie par, ponieważ pustka nie jest tak naprawdę typem.
Roberto Caboni,
2

Pod względem funkcjonalnym i składniowym, czy istnieje różnica między funkcją, której prototypem jest int foo (void) i int foo (void *)?

Jest różnica:

int foo(void) deklaruje funkcję, która nie przyjmuje żadnych argumentów.

int foo(void *)deklaruje funkcję, która akceptuje pojedynczy argument typu void*.

W C ++ int foo(void)jest równoważny int foo().

eerorika
źródło