Czym różni się odniesienie Java od wskaźnika C?

97

C ma wskaźniki, a Java ma tak zwane referencje. Mają pewne wspólne cechy w tym sensie, że wszystkie na coś wskazują. Wiem, że wskaźniki w C przechowują adresy, na które wskazują. Czy referencje przechowują również adres? Czym się różnią, tyle że wskaźnik jest bardziej elastyczny i podatny na błędy?

Gnijuohz
źródło
10
uwaga C ++ ma również odwołania, które różnią się od wskaźników lub referencji Java
jk.
@jk. Myślałem, że będzie tak samo jak w Javie. Jaka jest różnica?
Gnijuohz
17
Referencje w C ++ nie podlegają ponownemu wiązaniu (tzn. Nie można zmienić wyznaczonego obiektu) i nie są zerowalne (tzn. Nie można w żaden sposób prawidłowo odwoływać się do żadnego obiektu).
AProgrammer
@Gnijuohz Odświeżanie tego, co powiedział @AProgrammer: finalOdwołanie w Javie jest prawie równoważne odwołaniu do C ++. Powodem, dla którego nie są dokładnie równoważne, jest to, że odwołanie do C ++ nie jest również zerowalne, podczas gdy finalodwołanie w Javie jest zerowalne.
Utku

Odpowiedzi:

142

Referencje można zaimplementować, przechowując adres. Zazwyczaj odniesienia do Java będą implementowane jako wskaźniki, ale nie jest to wymagane przez specyfikację. Mogą używać dodatkowej warstwy pośredniej, aby ułatwić zbieranie śmieci. Ale ostatecznie (prawie zawsze) sprowadza się do wskaźników (w stylu C) zaangażowanych w implementację odniesień (w stylu Java).

Nie można wykonywać arytmetyki wskaźników za pomocą referencji. Najważniejszą różnicą między wskaźnikiem w C a referencją w Javie jest to, że nie można faktycznie uzyskać (i manipulować) wartości bazowej referencji w Javie. Innymi słowy: nie można wykonywać arytmetyki wskaźników.

W C możesz dodać coś do wskaźnika (tj. Adres) lub odjąć coś, aby wskazać rzeczy „w pobliżu” lub wskazać miejsca, które są w dowolnym miejscu.

W Javie odwołanie wskazuje tylko na jedną rzecz. Możesz sprawić, by zmienna zawierała inne odwołanie, ale nie możesz po prostu poprosić o wskazanie „rzecz po rzeczy oryginalnej”.

Referencje są mocno wpisane. Inną różnicą jest to, że typ odwołania jest znacznie bardziej ściśle kontrolowany w Javie niż typ wskaźnika w C. W C możesz mieć int*i rzutować na char*ai po prostu ponownie interpretować pamięć w tym miejscu. Ta reinterpretacja nie działa w Javie: możesz interpretować obiekt na drugim końcu referencji jako coś, co już jest (tzn. Możesz rzutować Objectreferencję na Stringreferencję tylko wtedy, gdy wskazany obiekt to a String).

Różnice te sprawiają, że wskaźniki C są silniejsze, ale także bardziej niebezpieczne. Obie te możliwości (arytmetyka wskaźnika i reinterpretacja wskazywanych wartości) zwiększają elastyczność C i są źródłem pewnej potęgi języka. Ale są również dużymi źródłami problemów, ponieważ jeśli użyte nieprawidłowo, mogą łatwo złamać założenia, na których opiera się Twój kod. I jest to dość łatwe w użyciu ich nieprawidłowo.

Joachim Sauer
źródło
18
+1 za potęgę . Nie polegaj na szczegółach implementacji.
CVn
2
+1 Czy odśmiecanie nie zasługuje na wzmiankę o konkretnym odważnym punkcie? Jest to kolejny sposób, w jaki wskaźniki C są bardziej wydajne, ale także bardziej niebezpieczne (ryzyko zwisania wskaźników do uwolnionej pamięci, powodując uszkodzenie pamięci, ryzyko wycieków pamięci)
MarkJ
Inną różnicą między odniesieniami i wskaźnikami jest to, że wskaźnik w C może być przekształcony w ciąg liczb (np. Przez memcpyprzeniesienie jednego do a char[]) i odwrotnie. Jeśli wskaźnik zostanie przekonwertowany na ciąg liczb, który jest przechowywany w innym miejscu (być może pokazany na ekranie i skopiowany przez operatora na kartce papieru), wszystkie kopie wskaźnika w komputerze zostaną zniszczone, a ta sekwencja liczb zostanie przekonwertowana wracając do wskaźnika (być może po wpisaniu przez operatora), wskaźnik nadal musi wskazywać to samo, co wcześniej. Program, który ...
supercat
... wyświetlał wskaźnik jako liczby, a następnie przekształcał ręcznie wprowadzane liczby na wskaźnik, może być „zły”, ale nie wywoływałby żadnego niezdefiniowanego zachowania, chyba że operator wpisałby liczby, które nie zostały pokazane jako prawidłowe wskaźnik. Dlatego w pełni przenośne C nie jest możliwe zbieranie śmieci do ogólnego użytku, ponieważ komputer nie może wiedzieć, czy gdzieś we wszechświecie istnieje kopia wskaźnika.
supercat
Według JLS, §4.3.1, referencje w Javie są wskaźnikami, więc „Zwykle referencje Java będą implementowane jako wskaźniki, ale nie jest to wymagane przez specyfikację”. to fałsz.
Lew Bloch,
8

Referencje w C ++ są znowu różne.

Muszą być zainicjowane i nie mogą mieć wartości zerowej (przynajmniej nie w dobrze uformowanym programie) i nie mogą być ponownie ustawione, aby odwoływać się do czegoś innego. Odwołanie do C ++ jest znacznie bardziej jak alias obiektu.

Inną ważną różnicą między wskaźnikami a referencjami Java / C ++ jest to, że możesz wziąć adres wskaźnika, do którego nie możesz uzyskać dostępu do adresu referencji (w rzeczywistości referencja C ++ wcale nie musi istnieć jako obiekt w pamięci), w związku z czym możesz mieć wskaźnik do wskaźnika, ale nie odniesienie do odniesienia

jk.
źródło
4

Referencje Java i wskaźniki C różnią się dokładnie dwoma punktami:

  1. W przypadku tego pierwszego nie ma arytmetyki wskaźnikowej.
  2. I nie możesz utworzyć odwołania do Java do czegokolwiek chcesz, możesz skopiować tylko te zapisane gdzieś dostępne (pola statyczne, pola obiektów, zmienne lokalne) lub zwrócone przez wywołania funkcji (takie jak wywołania konstruktora), które w ten sposób odnoszą się do Java przedmiotów (nigdy do podstawowych typów, takich jak referencje char, inti tak dalej).

Ktoś napisał, że odwołania są mocno wpisane, ponieważ nie można zmusić kompilatora do traktowania int*jako pliku char*.
Całkowicie pomijając fakt, że ta konkretna konwersja jest rzeczywiście bezpieczna , nie ma polimorfizmu w C, więc porównanie nie jest początkowe.
Z pewnością Java jest silniej typowana niż C, nie dlatego, że jest to cecha wskaźników C w porównaniu z referencjami Java, musisz użyć JNI do złamania bezpieczeństwa typu (oprócz ignorowania ogólnych ograniczeń), ale nawet w C musisz wymusić kompilator.

Ktoś napisał, że odniesienia Java może być realizowany jako wskaźniki C, do których mówię na pewno, ponieważ są one ściśle mniej wydajne, na maszynach 32bit one zazwyczaj są, jeśli JVM jest realizowany w C . Chociaż na maszynach 64 - bitowych są one zwykle skompresowanymi wskaźnikami zwykłych obiektów („skompresowanymi operacjami OOP”) w celu zaoszczędzenia miejsca i przepustowości.
W każdym razie te wskaźniki C nie muszą być równoważne adresom sprzętowym, nawet jeśli zazwyczaj (> 99% implementacji) są spowodowane wydajnością.
Wreszcie jest to szczegół implementacji, który nie jest ujawniany programistom.

Deduplikator
źródło
-1

Są nieco inne. W Javie kopia odwołania jest kopiowana na stos wywoływanej funkcji, wskazując ten sam obiekt co funkcję wywołującą i umożliwiając manipulowanie tym obiektem. Nie można jednak zmienić obiektu, do którego odnosi się funkcja wywołująca.

Rozważ następujący kod Java

public static void changeRValue(StringBuffer sb){
    sb = new StringBuffer("helllllo"); /*attempt to assign the reference
                                        to a new object*/
}
public static void main(String[] args) {
    StringBuffer sb = new StringBuffer("hi");     //Create a new string buffer
    changeRValue(sb);                             //Call changeRValue
    System.out.println(sb.toString());            //Prints "hi" not "hello"
}

Rozważmy teraz wskaźnik w c ++:

void func(Dog* dog){
    *dog = Dog("hello world"); //Change the value of dog to a new object
}

int main(int argc, const char * argv[]) {
    Dog dog1("hi");                            //Create a dog object
    func(&dog1);                               //pass the address of dog
    cout << dog1.name;                         //Prints "hello world" not hi.
    return 0;
}
Eladian
źródło
Pomyślałem, że mógłbym dodać, że może być postrzegany jako podobny do
stałego
2
Możesz modyfikować obiekt wskazany w Javie z taką samą łatwością jak w C ++. Musisz tylko mieć dostęp do odpowiednich członków. Obiekty Java nie mają operatorów przypisania, ponieważ Java nie obsługuje przeciążania operatora zdefiniowanego przez użytkownika, więc nie można użyć przeciążonego operatora, wielka sprawa. Ten brak Java nie ma nic wspólnego z faktem, że odwołania do Javy są takie same, jak wskaźniki C ++ pozbawione arytmetyki wskaźników.
Deduplicator,