Dlaczego void w C oznacza nie void?

25

W silnie typowanych językach, takich jak Java i C #, void(lub Void) jako typ zwracany dla metody wydaje się oznaczać:

Ta metoda niczego nie zwraca. Nic. Bez powrotu. Nic nie otrzymasz z tej metody.

Naprawdę dziwne jest to, że w C, voidjako typ zwracany lub nawet jako typ parametru metody oznacza:

To może być naprawdę wszystko. Musisz przeczytać kod źródłowy, aby się dowiedzieć. Powodzenia. Jeśli jest to wskaźnik, naprawdę powinieneś wiedzieć, co robisz.

Rozważ następujące przykłady w C:

void describe(void *thing)
{
    Object *obj = thing;
    printf("%s.\n", obj->description);
}

void *move(void *location, Direction direction)
{
    void *next = NULL;

    // logic!

    return next;
}

Oczywiście druga metoda zwraca wskaźnik, który z definicji może być dowolny.

Skoro C jest starsze niż Java i C #, dlaczego te języki przyjęły voidjako „nic”, podczas gdy C użył go jako „nic lub cokolwiek (gdy wskaźnik)”?

Naftuli Kay
źródło
138
W tytule pytania i tekście jest mowa o voidtym, void*czego używa przykład kodu, co jest czymś zupełnie innym.
4
Zauważ również, że Java i C # mają tę samą koncepcję, po prostu nazywają to Objectw jednym przypadku, aby ujednoznacznić.
Mooing Duck
4
@Mefy nie są dokładnie napisane? Przez C # przy użyciu pisania kaczego masz na myśli dynamictyp, który jest rzadko używany?
Axarydax,
9
@Mephy: Kiedy ostatnio sprawdziłem, w Wikipedii wymieniono jedenaście wzajemnie sprzecznych znaczeń dla „silnie wpisanego”. Mówienie, że „C # nie jest silnie typowane” jest zatem bez znaczenia.
Eric Lippert,
8
@LightnessRacesinOrbit: Nie, chodzi o to, że nie ma wiarygodnej definicji tego terminu. Wikipedia nie jest zasobem nakazowym, który mówi ci, jakie jest właściwe znaczenie tego terminu. Jest to zasób opisowy . Fakt, że opisuje jedenaście różnych sprzecznych znaczeń, jest dowodem na to , że tego terminu nie można używać w rozmowie bez jego dokładnego zdefiniowania . Postępowanie w inny sposób oznacza, że ​​szanse są bardzo dobre, ludzie w rozmowie będą rozmawiać o przeszłości, a nie o sobie nawzajem.
Eric Lippert,

Odpowiedzi:

58

Słowo kluczowe void(nie wskaźnik) oznacza w tych językach „nic”. To jest konsekwentne.

Jak zauważyłeś, void*oznacza „wskaźnik do czegokolwiek” w językach, które obsługują surowe wskaźniki (C i C ++). Jest to niefortunna decyzja, ponieważ, jak wspomniałeś, oznacza voidto dwie różne rzeczy.

Nie byłem w stanie znaleźć historycznego powodu ponownego użycia, voidaby oznaczać „nic” i „cokolwiek” w różnych kontekstach, jednak C robi to w kilku innych miejscach. Na przykład staticma różne cele w różnych kontekstach. Oczywiście w języku C istnieje precedens w zakresie ponownego wykorzystywania słów kluczowych w ten sposób, niezależnie od tego, co można pomyśleć o praktyce.

Java i C # są wystarczająco różne, aby zrobić czystą przerwę w celu rozwiązania niektórych z tych problemów. Java i „bezpieczne” C # także nie pozwalają surowych wskaźników i nie potrzebują łatwego kompatybilność C (Niebezpieczne C # nie pozwalają wskazówek, ale zdecydowana większość kodu C # nie należą do tej kategorii). Pozwala im to nieco zmienić sytuację bez obawy o zgodność wsteczną. Jednym ze sposobów na to jest wprowadzenie klasy Objectu podstawy hierarchii, z której dziedziczą wszystkie klasy, więc Objectodwołanie służy tej samej funkcji void*bez nieprzyjemności związanych z typami i zarządzaniem surową pamięcią.


źródło
2
4
They also do not allow raw pointers and do not need easy C compatibility.Fałszywe. C # ma wskaźniki. Zwykle są (i zwykle powinny być) wyłączone, ale są tam.
Mag
8
Jeśli chodzi o powód ponownego voidużycia: oczywistym powodem byłoby uniknięcie wprowadzenia nowego słowa kluczowego (i potencjalne uszkodzenie istniejących programów). Gdyby wprowadzili specjalne słowo kluczowe (np. unknown), void*Byłby to bezsensowny (i być może nielegalny) konstrukt i unknownbyłby legalny tylko w formie unknown*.
jamesdlin,
20
Nawet w void *, voidoznacza „nic”, a nie „o niczym”. Nie możesz wyłuskać a void *, musisz najpierw rzucić go na inny typ wskaźnika.
oefe
2
@oefe myślę, że to nie ma sensu, aby podzielić te włosy, bo voidi void*są różne typy (faktycznie, void„nie ma typ”). Ma to tyle samo sensu, co powiedzenie „ coś intw int*środku znaczy”. Nie, po zadeklarowaniu zmiennej wskaźnikowej mówisz „jest to adres pamięci zdolny do przechowywania czegoś”. W przypadku: void*mówisz „ten adres zawiera coś, ale czegoś nie można wywnioskować z rodzaju wskaźnika”.
32

voidi void*są dwie różne rzeczy. voidw C oznacza dokładnie to samo, co w Javie, brak zwracanej wartości. A void*jest wskaźnikiem z brakiem typu.

Wszystkie wskaźniki w C muszą być dereferencyjne. Jeśli zrezygnujesz z a void*, jakiego rodzaju możesz się spodziewać? Pamiętaj, że wskaźniki C nie niosą żadnych informacji o typie środowiska wykonawczego, więc typ musi być znany w czasie kompilacji.

Biorąc pod uwagę ten kontekst, jedyną rzeczą, którą możesz logicznie zrobić z dereferencją, void*jest zignorowanie go, a dokładnie takie zachowanie voidoznacza ten typ.

Karl Bielefeldt
źródło
1
Zgadzam się, dopóki nie przejdziesz do fragmentu „zignoruj”. Możliwe, że zwracana rzecz zawiera informacje na temat jej interpretacji. Innymi słowy, może to być odpowiednik C wariantu BASIC. „Kiedy to dostaniesz, sprawdź, co to jest - nie mogę ci powiedzieć z góry, co znajdziesz”.
Floris,
1
Innymi słowy, hipotetycznie mógłbym umieścić zdefiniowany przez programistę „deskryptor typu” w pierwszym bajcie w miejscu pamięci zaadresowanym przez wskaźnik, który powiedziałby mi, jakiego rodzaju danych mogę się spodziewać w kolejnych bajtach.
Robert Harvey
1
Oczywiście, ale w C postanowiono pozostawić te szczegóły programistom, zamiast upiec je w języku. Z punktu widzenia języka nie wie, co robić poza ignorowaniem. Pozwala to uniknąć narzutu związanego z włączaniem informacji o typie jako pierwszego bajtu, gdy w większości przypadków użycia można je ustalić statycznie.
Karl Bielefeldt
4
@RobertHarvey tak, ale wtedy nie dereferentujesz pustego wskaźnika; rzutujesz wskaźnik pustki na wskaźnik char, a następnie odsuwasz wskaźnik char.
user253751
4
@Floris gccnie zgadza się z tobą. Jeśli spróbujesz użyć tej wartości, gccwyświetl komunikat o błędzie error: void value not ignored as it ought to be.
kasperd
19

Być może bardziej przydatne byłoby myślenie o voidtypie zwrotu. Następnie twoja druga metoda brzmiałaby „metoda, która zwraca nietypowy wskaźnik”.

Robert Harvey
źródło
To pomaga. Jako osoba, która (wreszcie!) Uczy się języka C po latach spędzonych w językach zorientowanych obiektowo, wskaźniki są dla mnie zupełnie nową koncepcją. Wygląda na to, że zwracany wskaźnik voidjest mniej więcej odpowiednikiem *języków takich jak ActionScript 3, co oznacza po prostu „dowolny typ zwracany”.
Naftuli Kay
5
Cóż, voidto nie jest symbol wieloznaczny. Wskaźnik to tylko adres pamięci. Umieszczenie typu przed *napisem „oto typ danych, którego można się spodziewać pod adresem pamięci, do którego odnosi się ten wskaźnik”. Stawianie voidprzed *kompilatorem mówi: „Nie znam typu; po prostu zwróć mi surowy wskaźnik, a wymyślę, co zrobić z danymi, które on wskazuje”.
Robert Harvey
2
Powiedziałbym nawet, że void*wskazuje na „pustkę”. Nagi wskaźnik bez niczego, na co wskazuje. Nadal możesz rzucić go jak każdy inny wskaźnik.
Robert,
11

Zaostrzmy trochę terminologię.

Z internetowego standardu C 2011 :

6.2.5 Typy
...
19 voidTyp zawiera pusty zestaw wartości; jest to niekompletny typ obiektu, którego nie można uzupełnić.
...
6.3 Konwersje
...
6.3.2.2 void

1 Wartość (nieistniejąca) voidwyrażenia (wyrażenie typu void) nie może być w żaden sposób wykorzystywana, a niejawne lub jawne konwersje (z wyjątkiem do void) nie mają zastosowania do takie wyrażenie. Jeśli wyrażenie innego typu jest oceniane jako void wyrażenie, jego wartość lub oznaczenie są odrzucane. ( voidWyrażenie jest oceniane pod kątem skutków ubocznych.)

6.3.2.3 Wskaźniki

1 Wskaźnik dovoidmoże zostać przekonwertowany na lub ze wskaźnika na dowolny typ obiektu. Wskaźnik do dowolnego typu obiektu można przekonwertować na wskaźnik, aby anulować i ponownie; wynik powinien być równy pierwotnemu wskaźnikowi.

voidWyrażenie ma wartość (choć może to mieć skutki uboczne). Jeśli mam zdefiniowaną funkcję do zwrócenia void, tak:

void foo( void ) { ... }

potem połączenie

foo();

nie ocenia wartości; Nie mogę przypisać wyniku do niczego, ponieważ nie ma wyniku.

Wskaźnik na voidto w istocie „rodzajowe” typ wskaźnik; możesz przypisać wartość void *do dowolnego innego typu wskaźnika obiektu bez potrzeby jawnego rzutowania (dlatego wszyscy programiści C będą krzyczeć na ciebie za rzutowanie wyniku malloc).

Nie możesz bezpośrednio wyłuskać a void *; musisz najpierw przypisać go do innego typu wskaźnika obiektu, aby uzyskać dostęp do wskazanego obiektu.

voidwskaźniki są używane do implementacji (mniej lub bardziej) ogólnych interfejsów; kanonicznym przykładem jest qsortfunkcja biblioteczna, która może sortować tablice dowolnego typu, o ile udostępnia się funkcję porównania uwzględniającą typy.

Tak, użycie tego samego słowa kluczowego dla dwóch różnych pojęć (bez wartości vs. ogólny wskaźnik) jest mylące, ale nie jest tak, że nie ma precedensu; staticma wiele różnych znaczeń zarówno w C, jak i C ++.

John Bode
źródło
9

W Javie i C # void jako typ zwracany dla metody wydaje się oznaczać: ta metoda niczego nie zwraca. Nic. Bez powrotu. Nic nie otrzymasz z tej metody.

To stwierdzenie jest poprawne. Dotyczy to również C i C ++.

w C, void jako typ zwracany lub nawet jako typ parametru metody oznacza coś innego.

To stwierdzenie jest nieprawidłowe. voidjako typ zwracany w C lub C ++ oznacza to samo, co robi w C # i Javie. Mylisz się voidz void*. Oni są zupełnie inni.

Czy to nie jest mylące voidi void*oznacza dwie zupełnie różne rzeczy?

Tak.

Co to jest wskaźnik? Co to jest wskaźnik pustki?

Wskaźnikiem jest wartość , która może być dereferencjonowane . Dereferencing jest ważny wskaźnik daje lokalizację magazynu z następujących spiczasty do wpisywania . Nieważne wskaźnik jest wskaźnikiem, który nie ma szczególności wskazywanego wpisywać; musi zostać przekonwertowany na bardziej szczegółowy typ wskaźnika, zanim dereferencja wartości spowoduje utworzenie parametru przechowywania .

Czy wskaźniki Void są takie same w C, C ++, Java i C #?

Java nie ma pustych wskaźników; C # robi. Są one takie same jak w C i C ++ - wartość wskaźnika, która nie jest powiązana z żadnym konkretnym typem, który musi zostać przekonwertowany na bardziej konkretny typ przed wyrejestrowaniem go, aby utworzyć miejsce przechowywania.

Skoro C jest starsze niż Java i C #, dlaczego te języki przyjęły pustkę jako oznaczającą „nic”, podczas gdy C użył go jako „nic lub cokolwiek (gdy wskaźnik)”?

Pytanie jest niespójne, ponieważ zakłada fałsz. Zadajmy lepsze pytania.

Dlaczego Java i C # przyjęły konwencję, która voidjest prawidłowym typem zwracania, zamiast na przykład używać konwencji Visual Basic, że funkcje nigdy nie zwracają nieważności, a podprogramy zawsze zwracają nieważność?

Zapoznanie się z programistami przybywającymi do Java lub C # z języków, w których void jest typ zwracany.

Dlaczego C # przyjął mylącą konwencję, która void*ma zupełnie inne znaczenie niżvoid ?

Znajomość programistów przybywających do C # z języków, w których void*jest typ wskaźnika.

Eric Lippert
źródło
5
void describe(void *thing);
void *move(void *location, Direction direction);

Pierwsza funkcja nic nie zwraca. Druga funkcja zwraca wskaźnik pustki. Zadeklarowałbym tę drugą funkcję jako

void* move(void* location, Direction direction);

Jeśli może pomóc, jeśli myślisz, że „pustka” oznacza „bez znaczenia”. Zwracana wartość describeto „bez znaczenia”. Zwracanie wartości z takiej funkcji jest nielegalne, ponieważ powiedziałeś kompilatorowi, że zwracana wartość nie ma znaczenia. Nie można przechwycić wartości zwracanej z tej funkcji, ponieważ nie ma ona znaczenia. Nie można zadeklarować zmiennej typu, voidponieważ nie ma ona znaczenia. Na przykład void nonsense;jest nonsensem i jest w rzeczywistości nielegalny. Zauważ też, że tablica pustkivoid void_array[42]; jest również nonsensem.

Wskaźnik do void ( void*) to coś innego. Jest to wskaźnik, a nie tablica. Czytaćvoid* jako oznaczający wskaźnik, który wskazuje na coś „bez znaczenia”. Wskaźnik jest „bez znaczenia”, co oznacza, że ​​nie ma sensu rezygnować z takiego wskaźnika. Próbowanie tego jest w rzeczywistości nielegalne. Kod wykluczający wskaźnik pustki nie zostanie skompilowany.

Więc jeśli nie możesz wyłapać wskaźnika pustki i nie możesz zrobić szeregu pustek, jak możesz ich użyć? Odpowiedź jest taka, że ​​wskaźnik do dowolnego typu można rzutować do iz niego void*. Rzucenie wskaźnika na void*i powrót do wskaźnika do oryginalnego typu da wartość równą oryginalnemu wskaźnikowi. Standard C gwarantuje takie zachowanie. Głównym powodem, dla którego widzisz tak wiele pustych wskaźników w C, jest to, że ta zdolność zapewnia sposób na ukrywanie informacji i programowanie obiektowe w bardzo zorientowanym na obiekt języku.

Wreszcie, powodem często można zobaczyć movezadeklarowany jako void *move(...)raczej niż void* move(...)jest dlatego oddanie gwiazdkę obok nazwy zamiast typu jest bardzo częstą praktyką w C. Powodem jest to, że po deklaracji będzie Ci w tarapatach: int* iptr, jptr;To sprawi, że myślę, że deklarujesz dwa wskaźniki do int. Nie jesteś. Ta deklaracja jptrjest intraczej wskaźnikiem niż int. Właściwą deklaracją jest int *iptr, *jptr;to, że możesz udawać, że gwiazdka należy do tego typu, jeśli trzymasz się zasady deklarowania tylko jednej zmiennej na instrukcję deklaracji. Pamiętaj jednak, że udajesz.

David Hammen
źródło
„Kod, który wyklucza wskaźnik zerowy, nie zostanie skompilowany”. Myślę, że masz na myśli kod, który odrzuca wskaźnik pustki , którego nie można skompilować. Kompilator nie chroni cię przed dereferencją wskaźnika zerowego; to jest problem środowiska wykonawczego.
Cody Gray
@CodyGray - Dzięki! Naprawiono to. Kod, który wyklucza wskaźnik zerowy, zostanie skompilowany (o ile wskaźnik nie jest void* null_ptr = 0;).
David Hammen
2

Krótko mówiąc, nie ma różnicy . Dam ci kilka przykładów.

Powiedz, że mam klasę o nazwie Samochód.

Car obj = anotherCar; // obj is now of type Car
Car* obj = new Car(); // obj is now a pointer of type Car
void obj = 0; // obj has no type
void* = new Car(); // obj is a pointer with no type

Wierzę tu zamieszanie tutaj opiera się w jaki sposób można zdefiniować voidvs void*. Zwracany typ voidoznacza „Zwracam nic”, podczas gdy typ zwracany void*oznacza „Zwracam wskaźnik typu nic”.

Wynika to z faktu, że wskaźnik jest szczególnym przypadkiem. Obiekt wskaźnika zawsze będzie wskazywał lokalizację w pamięci, niezależnie od tego, czy jest tam prawidłowy obiekt, czy nie. To, czy moje void* carodniesienia dotyczą bajtu, słowa, samochodu czy roweru, nie ma znaczenia, ponieważ moje void*punkty wskazują na coś bez określonego typu. To od nas zależy później.

W językach takich jak C # i Java zarządzana jest pamięć, a koncepcja wskaźników jest przenoszona do klasy Object. Zamiast mieć odniesienie do obiektu typu „nic” wiemy, że przynajmniej nasz obiekt jest typu „Obiekt”. Pozwól mi wyjaśnić dlaczego.

W tradycyjnym C / C ++, jeśli utworzysz obiekt i usuniesz jego wskaźnik, nie będzie można uzyskać dostępu do tego obiektu. Jest to tak zwany wyciek pamięci .

W języku C # / Java wszystko jest obiektem, co umożliwia środowisku wykonawczemu śledzenie każdego obiektu. Nie musi koniecznie wiedzieć, że mój obiekt jest samochodem, po prostu musi znać podstawowe informacje, którymi zajmuje się już klasa Object.

Dla nas, programistów, oznacza to, że większość informacji, które musielibyśmy ręcznie obsługiwać w C / C ++, są obsługiwane przez klasę Object, co eliminuje potrzebę specjalnego przypadku, jakim jest wskaźnik.

Thebluefish
źródło
„moja pustka * wskazuje na coś bez określonego typu” - niezupełnie. Bardziej słuszne jest stwierdzenie, że jest to wskaźnik do wartości zerowej długości (prawie jak tablica z 0 elementami, ale bez informacji o typie związanych z tablicą).
Scott Whitlock,
2

Spróbuję powiedzieć, jak można by pomyśleć o tych rzeczach w C. Oficjalny język w standardzie C nie jest zbyt swobodny voidi nie będę się starał być z nim całkowicie zgodny.

Ten typ voidnie jest obywatelem pierwszej klasy wśród typów. Chociaż jest to typ obiektu, nie może być typem żadnego obiektu (lub wartości), pola lub parametru funkcji; może to jednak być typ zwracany przez funkcję, a tym samym typ wyrażenia (w zasadzie wywołania takich funkcji, ale wyrażenia utworzone za pomocą operatora warunkowego mogą mieć również typ void). Ale nawet minimalne użycie voidjako typu wartości, dla którego powyższe pozostawia otwarte drzwi, a mianowicie zakończenie funkcji zwracającej voidwyrażenie w postaci, w return E;której Ejest wyrażeniem typu void, jest wyraźnie zabronione w C (jest to jednak dozwolone w C ++, ale z przyczyn nie mających zastosowania do C).

Gdyby voiddozwolone były obiekty typu , miałyby one 0 bitów (to jest ilość informacji w wartości wyrażenia voidtypu); dopuszczenie takich obiektów nie stanowiłoby większego problemu, ale byłyby raczej bezużyteczne (obiekty o rozmiarze 0 utrudniłyby zdefiniowanie arytmetyki wskaźnika, co najlepiej byłoby po prostu zabronione). Zbiór różnych wartości takiego obiektu miałby jeden (trywialny) element; jego wartość można przyjąć, trywialnie, ponieważ nie ma żadnej substancji, ale nie można jej modyfikować (brak innej wartości). Norma jest zatem mylona, ​​mówiąc, że voidzawiera pusty zestaw wartości; taki typ mógłby być użyteczny do opisania wyrażeń, które nie mogąbyć oceniane (takie jak skoki lub nie kończące się połączenia), chociaż język C tak naprawdę nie używa takiego typu.

Niedozwolony jako typ wartości voidzostał użyty co najmniej na dwa niezwiązane bezpośrednio sposoby oznaczenia „zabronione”: określenie (void)jako specyfikacja parametru w typach funkcji oznacza, że ​​podawanie im jakichkolwiek argumentów jest zabronione (podczas gdy „()” oznaczałoby wszystko jest dozwolone i tak, nawet podanie voidwyrażenia jako argumentu funkcji ze (void)specyfikacją parametru jest zabronione), a zadeklarowanie void *poznacza, że ​​wyrażenie dereferencyjne *pjest zabronione (ale nie jest to prawidłowe wyrażenie typu void). Masz więc rację, że te zastosowania voidnie są tak naprawdę zgodne z voidprawidłowym typem wyrażeń. Jednak obiekt typuvoid*tak naprawdę nie jest wskaźnikiem do wartości dowolnego typu, oznacza wartość, która jest traktowana jako wskaźnik, chociaż nie można go wyłuskać, a arytmetyka wskaźnika z nią jest zabroniona. „Traktowany jako wskaźnik” oznacza tak naprawdę tylko, że można go rzutować zi na dowolny typ wskaźnika bez utraty informacji. Można go jednak również użyć do przesyłania wartości całkowitych do iz, więc nie musi w ogóle wskazywać na nic.

Marc van Leeuwen
źródło
Ściśle mówiąc, można go rzutować zi na dowolny typ wskaźnika obiektu bez utraty informacji, ale nie zawsze do i od . Zasadniczo rzutowanie z void*innego typu wskaźnika może utracić informacje (lub nawet mieć UB, nie pamiętam z ręki), ale jeśli wartość void*pochodzi z rzutowania wskaźnika tego samego typu, na który teraz rzucasz, to nie ma.
Steve Jessop
0

W języku C # i Javie każda klasa jest pochodną Objectklasy. Dlatego za każdym razem, gdy chcemy przekazać odniesienie do „czegoś”, możemy użyć odwołania typu Object.

Ponieważ nie musimy używać do tego celu pustych wskaźników w tych językach, voidnie musi tutaj oznaczać „coś albo nic”.

Reg Edycja
źródło
0

W C można mieć wskaźnik do rzeczy dowolnego typu [wskaźniki zachowują się jak rodzaj odniesienia]. Wskaźnik może zidentyfikować obiekt o znanym typie lub obiekt o dowolnym (nieznanym) typie. Twórcy C wybrali tę samą ogólną składnię dla „wskaźnika do rzeczy dowolnego typu”, jak dla „wskaźnika do rzeczy określonego typu”. Ponieważ składnia w tym drugim przypadku wymaga tokena do określenia, jaki jest typ, poprzednia składnia wymaga umieszczenia czegoś tam, gdzie należałby ten token, gdyby typ był znany. C wybrał do tego celu słowo kluczowe „void”.

W Pascalu, podobnie jak w C, możliwe jest posiadanie wskaźników do rzeczy dowolnego typu; bardziej popularne dialekty Pascala pozwalają również na „wskaźnik do rzeczy dowolnego typu”, ale zamiast używać tej samej składni, jak w przypadku „wskaźnika do rzeczy określonego typu”, po prostu określają to pierwsze jako Pointer.

W Javie nie ma typów zmiennych zdefiniowanych przez użytkownika; wszystko jest albo pierwotnym, albo odniesieniem do obiektu sterty. Można zdefiniować rzeczy typu „odniesienie do obiektu sterty określonego typu” lub „odwołanie do obiektu sterty dowolnego typu”. Pierwszy używa po prostu nazwy typu bez specjalnej interpunkcji, aby wskazać, że jest to odwołanie (ponieważ nie może być niczym innym); ten drugi używa nazwy typu Object.

Użycie void*jako nomenklatury dla wskaźnika do dowolnego rodzaju jest, o ile wiem, unikalne dla C i pochodnych bezpośrednich, takich jak C ++; inne języki, które znam z tego pojęcia, po prostu używając wyraźnie nazwanego typu.

supercat
źródło
-1

Możesz pomyśleć o tym słowie kluczowym voidjak pustym struct(w C99 puste struktury są najwyraźniej niedozwolone , w .NET i C ++ zajmują więcej niż 0 pamięci, a na koniec przypominam sobie, że wszystko w Javie jest klasą):

struct void {
};

... co oznacza, że ​​jest to typ o długości 0. Tak to działa, gdy wykonujesz wywołanie funkcji, które zwraca void... zamiast przydzielić 4 bajty lub więcej na stosie dla wartości całkowitej zwracanej, nie zmienia to wcale wskaźnika stosu (zwiększa go o długość 0 ).

Jeśli więc zadeklarowałeś niektóre zmienne, takie jak:

int a;
void b;
int c;

... a następnie wziąłeś adres tych zmiennych, być może bi cmoże znajdować się w tym samym miejscu w pamięci, ponieważ bma zerową długość. Zatem a void*jest wskaźnikiem pamięci do wbudowanego typu o zerowej długości. Fakt, że możesz wiedzieć, że zaraz po tym jest coś, co może ci się przydać, jest inną sprawą i wymaga od ciebie, aby naprawdę wiedzieć, co robisz (i jest niebezpieczne).

Scott Whitlock
źródło
Jedynym znanym mi kompilatorem, który przypisuje długość do typu void, jest gcc, a gcc przypisuje mu długość 1.
Joshua