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, void
jako 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 void
jako „nic”, podczas gdy C użył go jako „nic lub cokolwiek (gdy wskaźnik)”?
void
tym,void*
czego używa przykład kodu, co jest czymś zupełnie innym.Object
w jednym przypadku, aby ujednoznacznić.dynamic
typ, który jest rzadko używany?Odpowiedzi:
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ś, oznaczavoid
to dwie różne rzeczy.Nie byłem w stanie znaleźć historycznego powodu ponownego użycia,
void
aby oznaczać „nic” i „cokolwiek” w różnych kontekstach, jednak C robi to w kilku innych miejscach. Na przykładstatic
ma 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
Object
u podstawy hierarchii, z której dziedziczą wszystkie klasy, więcObject
odwołanie służy tej samej funkcjivoid*
bez nieprzyjemności związanych z typami i zarządzaniem surową pamięcią.źródło
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.void
uż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 iunknown
byłby legalny tylko w formieunknown*
.void *
,void
oznacza „nic”, a nie „o niczym”. Nie możesz wyłuskać avoid *
, musisz najpierw rzucić go na inny typ wskaźnika.void
ivoid*
są różne typy (faktycznie,void
„nie ma typ”). Ma to tyle samo sensu, co powiedzenie „ cośint
wint*
ś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”.void
ivoid*
są dwie różne rzeczy.void
w C oznacza dokładnie to samo, co w Javie, brak zwracanej wartości. Avoid*
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 zachowanievoid
oznacza ten typ.źródło
gcc
nie zgadza się z tobą. Jeśli spróbujesz użyć tej wartości,gcc
wyświetl komunikat o błędzieerror: void value not ignored as it ought to be
.Być może bardziej przydatne byłoby myślenie o
void
typie zwrotu. Następnie twoja druga metoda brzmiałaby „metoda, która zwraca nietypowy wskaźnik”.źródło
void
jest mniej więcej odpowiednikiem*
języków takich jak ActionScript 3, co oznacza po prostu „dowolny typ zwracany”.void
to 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”. Stawianievoid
przed*
kompilatorem mówi: „Nie znam typu; po prostu zwróć mi surowy wskaźnik, a wymyślę, co zrobić z danymi, które on wskazuje”.void*
wskazuje na „pustkę”. Nagi wskaźnik bez niczego, na co wskazuje. Nadal możesz rzucić go jak każdy inny wskaźnik.Zaostrzmy trochę terminologię.
Z internetowego standardu C 2011 :
void
Wyrażenie ma wartość (choć może to mieć skutki uboczne). Jeśli mam zdefiniowaną funkcję do zwróceniavoid
, tak:potem połączenie
nie ocenia wartości; Nie mogę przypisać wyniku do niczego, ponieważ nie ma wyniku.
Wskaźnik na
void
to 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 wynikumalloc
).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.void
wskaźniki są używane do implementacji (mniej lub bardziej) ogólnych interfejsów; kanonicznym przykładem jestqsort
funkcja 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;
static
ma wiele różnych znaczeń zarówno w C, jak i C ++.źródło
To stwierdzenie jest poprawne. Dotyczy to również C i C ++.
To stwierdzenie jest nieprawidłowe.
void
jako typ zwracany w C lub C ++ oznacza to samo, co robi w C # i Javie. Mylisz sięvoid
zvoid*
. Oni są zupełnie inni.Tak.
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 .
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.
Pytanie jest niespójne, ponieważ zakłada fałsz. Zadajmy lepsze pytania.
Zapoznanie się z programistami przybywającymi do Java lub C # z języków, w których
void
jest typ zwracany.Znajomość programistów przybywających do C # z języków, w których
void*
jest typ wskaźnika.źródło
Pierwsza funkcja nic nie zwraca. Druga funkcja zwraca wskaźnik pustki. Zadeklarowałbym tę drugą funkcję jako
Jeśli może pomóc, jeśli myślisz, że „pustka” oznacza „bez znaczenia”. Zwracana wartość
describe
to „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,void
ponieważ nie ma ona znaczenia. Na przykładvoid 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 navoid*
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ć
move
zadeklarowany jakovoid *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 doint
. Nie jesteś. Ta deklaracjajptr
jestint
raczej wskaźnikiem niżint
. Właściwą deklaracją jestint *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.źródło
void* null_ptr = 0;
).Krótko mówiąc, nie ma różnicy . Dam ci kilka przykładów.
Powiedz, że mam klasę o nazwie Samochód.
Wierzę tu zamieszanie tutaj opiera się w jaki sposób można zdefiniować
void
vsvoid*
. Zwracany typvoid
oznacza „Zwracam nic”, podczas gdy typ zwracanyvoid*
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* car
odniesienia dotyczą bajtu, słowa, samochodu czy roweru, nie ma znaczenia, ponieważ mojevoid*
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.
źródło
Spróbuję powiedzieć, jak można by pomyśleć o tych rzeczach w C. Oficjalny język w standardzie C nie jest zbyt swobodny
void
i nie będę się starał być z nim całkowicie zgodny.Ten typ
void
nie 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życievoid
jako typu wartości, dla którego powyższe pozostawia otwarte drzwi, a mianowicie zakończenie funkcji zwracającejvoid
wyrażenie w postaci, wreturn E;
którejE
jest wyrażeniem typuvoid
, jest wyraźnie zabronione w C (jest to jednak dozwolone w C ++, ale z przyczyn nie mających zastosowania do C).Gdyby
void
dozwolone były obiekty typu , miałyby one 0 bitów (to jest ilość informacji w wartości wyrażeniavoid
typu); 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, żevoid
zawiera 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
void
został 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 podanievoid
wyrażenia jako argumentu funkcji ze(void)
specyfikacją parametru jest zabronione), a zadeklarowanievoid *p
oznacza, że wyrażenie dereferencyjne*p
jest zabronione (ale nie jest to prawidłowe wyrażenie typuvoid
). Masz więc rację, że te zastosowaniavoid
nie są tak naprawdę zgodne zvoid
prawidł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.źródło
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.W języku C # i Javie każda klasa jest pochodną
Object
klasy. Dlatego za każdym razem, gdy chcemy przekazać odniesienie do „czegoś”, możemy użyć odwołania typuObject
.Ponieważ nie musimy używać do tego celu pustych wskaźników w tych językach,
void
nie musi tutaj oznaczać „coś albo nic”.źródło
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.źródło
Możesz pomyśleć o tym słowie kluczowym
void
jak pustymstruct
(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ą):... 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:
... a następnie wziąłeś adres tych zmiennych, być może
b
ic
może znajdować się w tym samym miejscu w pamięci, ponieważb
ma zerową długość. Zatem avoid*
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).źródło