Co oznacza void* i jak go używać?

Dzisiaj, kiedy czytałem cudzy kod, zobaczyłem coś w stylu void *func(void* i);, co to void* oznacza tutaj dla nazwy funkcji i dla typu zmiennej, odpowiednio?

Ponadto, kiedy musimy używać tego rodzaju wskaźnika i jak go używać?

 158
c
Author: Robert Harvey, 2012-07-24

10 answers

Wskaźnik do void jest typem wskaźnika "generycznego". A void * może być przekonwertowany na dowolny inny typ wskaźnika bez wyraźnego oddania. Nie możesz dereferować void * ani arytmetyki wskaźnika; musisz najpierw przekonwertować wskaźnik na kompletny typ danych.

void * jest często używany w miejscach, w których musisz być w stanie pracować z różnymi typami wskaźników w tym samym kodzie. Jednym z najczęściej cytowanych przykładów jest funkcja biblioteczna qsort:

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

base jest adres tablica, nmemb jest liczbą elementów w tablicy, {[11] } jest wielkością każdego elementu, a {[12] } jest wskaźnikiem do funkcji porównującej dwa elementy tablicy. Nazywa się tak:

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);

Wyrażenia tablicowe iArr, dArr, i {[15] } są domyślnie konwertowane z typów tablic na typy wskaźników w wywołaniu funkcji, a każdy z nich jest domyślnie konwertowany z " wskaźnik do int/double/long" do " wskaźnik do void".

Funkcje porównawcze wyglądałyby jak coś like:

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;
}

Akceptując void *, qsort może pracować z tablicami dowolnego typu.

Wadą używania void * jest to, że wyrzuca się zabezpieczenie typu przez okno i w nadjeżdżający z naprzeciwka ruch. Nie ma nic, co uchroni Cię przed użyciem niewłaściwej procedury porównywania: {]}

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

compareInt oczekuje, że jego argumenty będą wskazywać na int s, ale w rzeczywistości działa z double s. nie ma sposobu, aby złapać ten problem podczas kompilacji; po prostu skończysz z missorted array.

 188
Author: John Bode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-25 22:36:26

Użycie void * oznacza, że funkcja może przyjmować wskaźnik, który nie musi być określonym typem. Na przykład, w funkcjach gniazd, masz

send(void * pData, int nLength)

Oznacza to, że można go wywołać na wiele sposobów, na przykład

char * data = "blah";
send(data, strlen(data));

POINT p;
p.x = 1;
p.y = 2;
send(&p, sizeof(POINT));
 23
Author: TheSteve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 08:20:26

C jest niezwykły pod tym względem. Można powiedzieć void czy nicość void* jest wszystkim (może być wszystkim).

To tylko takie małe, co robi różnicę. Rene zwrócił na to uwagę. A void * jest wskaźnikiem do jakiejś lokalizacji. To, jak "zinterpretować", pozostaje użytkownikowi.

Jest to jedyny sposób, aby mieć nieprzezroczyste typy w C. bardzo widoczne przykłady można znaleźć np. w glib lub bibliotekach ogólnej struktury danych. Jest traktowany bardzo szczegółowo w " interfejsach C i wdrożenia".

Proponuję przeczytać cały rozdział i spróbować zrozumieć pojęcie wskaźnika, aby "dostać to".

 11
Author: Friedrich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-22 09:59:10
void*

Jest 'wskaźnikiem do pamięci bez założeń jaki typ jest tam przechowywany'. Możesz użyć na przykład, jeśli chcesz przekazać argument funkcji i ten argument może być kilku typów, a w funkcji będziesz obsługiwał każdy typ.

 7
Author: René Kolařík,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 08:17:05

Możesz rzucić okiem na ten artykuł o wskaźnikach http://www.cplusplus.com/doc/tutorial/pointers/{[2] } i przeczytaj rozdział: void pointers .

To również działa dla języka C.

Wskaźnik typu void jest specjalnym typem wskaźnika. W C++, void reprezentuje Brak Typu, więc wskaźniki void są wskaźnikami, które wskaż wartość, która nie ma Typu (A więc również nieokreślonego długość i nieokreślone właściwości dereferencyjne).

Pozwala to na wskazanie dowolnego typu danych z liczby całkowitej wartość lub zmiennoprzecinkowa do ciągu znaków. Ale w zamian mają wielkie ograniczenie: dane przez nie wskazywane nie mogą być bezpośrednio dereferenced (co jest logiczne, ponieważ nie mamy typu do dereferencji do), i z tego powodu zawsze będziemy musieli oddać adres w wskaźnik void do innego typu wskaźnika, który wskazuje na konkretny typ danych przed jego odwołaniem.

 3
Author: A.G.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 08:19:34

Wskaźnik void jest znany jako wskaźnik ogólny. Chciałbym wyjaśnić przykładowym scenariuszem pthread.

Funkcja wątku będzie miała prototyp jako

void *(*start_routine)(void*)

Projektanci API pthread rozważali wartości argumentu i zwracania funkcji wątku. Jeśli te rzeczy są generyczne, możemy wpisać cast do void * podczas wysyłania jako argument. podobnie wartość zwracana może być pobrana z void*(ale nigdy nie używałem wartości zwracanych z funkcji wątku).

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<NUM_THREADS; t++){
      //*** t will be type cast to void* and send as argument.
      rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);   
      if (rc){
         printf("ERROR; return code from pthread_create() is %d\n", rc);
         exit(-1);
      }
   }    
   /* Last thing that main() should do */
   pthread_exit(NULL);
}
 3
Author: Jeyaram,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 08:38:23

C11 standard (n1570) §6.2.2.3 al1 p55 mówi:

Wskaźnik do void może być zamieniony na lub ze wskaźnika na dowolny obiekt Typ. Wskaźnik do dowolnego typu obiektu może być konwertowany na wskaźnik do void I back again; wynik jest równy oryginałowi pointer.

Możesz użyć tego ogólnego wskaźnika do przechowywania wskaźnika do dowolnego typu obiektu, ale nie możesz używać z nim zwykłych operacji arytmetycznych i nie możesz go ignorować.

 2
Author: md5,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 08:21:42

A void* jest wskaźnikiem, ale typ, na który wskazuje, jest nieokreślony. Kiedy przekazujesz wskaźnik void do funkcji, musisz wiedzieć, jaki był jej typ, aby później przywrócić go do tego poprawnego typu w funkcji, aby go użyć. Zobaczysz przykłady w pthreads, które używają funkcji z dokładnie prototypem w twoim przykładzie, które są używane jako funkcja wątku. Następnie możesz użyć argumentu void* jako wskaźnika do wybranego ogólnego typu danych, a następnie przenieść go z powrotem do tego typu użyj funkcji wątku. Musisz być ostrożny, gdy używasz void pointers, ponieważ jeśli nie powrócisz do wskaźnika jego prawdziwego typu, możesz skończyć z różnego rodzaju problemami.

 1
Author: mathematician1975,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 08:21:42

Funkcja pobiera wskaźnik do dowolnego typu i zwraca taki.

 0
Author: glglgl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 08:18:29

Oznacza wskaźnik możesz użyć tego linku, aby uzyskać więcej informacji o wskaźniku http://www.cprogramming.com/tutorial/c/lesson6.html

 -2
Author: dilara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 08:21:57