Jaka jest różnica pomiędzy
- parametr przekazany przez odniesienie
- parametr przekazany przez wartość?
Czy mógłbyś podać jakieś przykłady?
language-agnostic
pass-by-reference
pass-by-value
Deduplikator
źródło
źródło
Odpowiedzi:
Przede wszystkim rozróżnienie „przekazywanie przez wartość vs. przekazywanie przez odniesienie” zdefiniowane w teorii CS jest obecnie nieaktualne, ponieważ technika pierwotnie zdefiniowana jako „przekazywanie przez odniesienie” od tego czasu nie sprzyjała i jest rzadko używana. 1
Nowsze języki 2 zwykle używają innej (ale podobnej) pary technik, aby osiągnąć te same efekty (patrz poniżej), co jest głównym źródłem zamieszania.
Drugim źródłem nieporozumień jest fakt, że w „przekazywaniu przez odniesienie” „odniesienie” ma węższe znaczenie niż ogólny termin „odniesienie” (ponieważ fraza poprzedza je).
Teraz autentyczna definicja to:
Gdy parametr jest przekazywany przez referencję , wywołujący i odbierający używają tej samej zmiennej dla parametru. Jeśli odbiorca modyfikuje zmienną parametru, efekt jest widoczny dla zmiennej dzwoniącego.
Gdy parametr jest przekazywany przez wartość , wywołujący i odbierający mają dwie niezależne zmienne o tej samej wartości. Jeśli odbiorca modyfikuje zmienną parametru, efekt nie jest widoczny dla dzwoniącego.
W tej definicji należy zwrócić uwagę na:
„Zmienna” oznacza tutaj samą zmienną wywołującą (lokalną lub globalną) - tzn. Jeśli przekażę zmienną lokalną przez odniesienie i przypiszę jej, zmienię samą zmienną wywołującą, a nie np. Cokolwiek, na co wskazuje, jeśli jest wskaźnikiem .
Znaczenie „odniesienia” w „przekazać przez odniesienie” . Różnica w stosunku do ogólnego terminu „odniesienie” polega na tym, że to „odniesienie” jest tymczasowe i dorozumiane. To, co w zasadzie otrzymuje callee, to „zmienna”, która jest w pewnym sensie „taka sama” jak pierwotna. To, jak konkretnie osiąga się ten efekt, jest nieistotne (np. Język może również ujawniać niektóre szczegóły implementacji - adresy, wskaźniki, dereferencje - to wszystko jest nieistotne; jeśli efekt netto jest taki, to jest to przejście przez odniesienie).
Teraz, w nowoczesnych językach, zmienne są zwykle „typami referencyjnymi” (inna koncepcja wymyślona później niż „przekazana przez referencję” i zainspirowana przez nią), tj. Rzeczywiste dane obiektowe są gdzieś przechowywane osobno (zwykle na stercie), i tylko „odniesienia” do niego są zawsze przechowywane w zmiennych i przekazywane jako parametry. 3)
Przekazanie takiego odniesienia mieści się w wartości przekazywanej, ponieważ wartość zmiennej jest technicznie samym odwołaniem, a nie obiektem, do którego się odnosi. Jednak efekt netto w programie może być taki sam, jak wartość przekazywana lub wartość referencyjna:
Jak widać, ta para technik jest prawie taka sama jak w definicji, tylko z poziomem pośrednim: wystarczy zastąpić „zmienną” „obiektem odniesienia”.
Nie ma dla nich żadnej uzgodnionej nazwy, co prowadzi do zniekształconych wyjaśnień, takich jak „wywołaj według wartości, gdzie wartość jest odniesieniem”. W 1975 r. Barbara Liskov zasugerowała termin „ współdzielenie obiektu przez obiekt ” (lub czasem po prostu „współdzielenie obiektu przez telefon”), chociaż nigdy się nie przyjął. Ponadto żadne z tych wyrażeń nie jest zbieżne z oryginalną parą. Nic dziwnego, że stare terminy zostały użyte ponownie w nieobecności czegoś lepszego, co doprowadziło do zamieszania. 4
UWAGA : Przez długi czas ta odpowiedź brzmiała:
Jest to w większości poprawne, z wyjątkiem węższego znaczenia „odniesienia” - jest ono zarówno tymczasowe, jak i niejawne (nie musi, ale jest wyraźne i / lub trwałe są dodatkowymi cechami, a nie częścią semantycznego przejścia przez odniesienie , jak wyjaśniono powyżej). Bliższą analogią byłoby przekazanie kopii dokumentu w porównaniu z zaproszeniem do pracy nad oryginałem.
1 O ile nie programujesz w Fortran lub Visual Basic, nie jest to zachowanie domyślne, aw większości języków we współczesnym użyciu prawdziwe wywołanie referencyjne nie jest nawet możliwe.
2 Wspiera go także spora liczba starszych
3 W kilku współczesnych językach wszystkie typy są typami referencyjnymi. To podejście zostało wprowadzone przez CLU w 1975 roku i od tego czasu zostało przyjęte przez wiele innych języków, w tym Python i Ruby. Wiele innych języków stosuje podejście hybrydowe, w którym niektóre typy są „typami wartości”, a inne są „typami referencyjnymi” - wśród nich są C #, Java i JavaScript.
4 Nie ma nic złego w recyklingu starego, odpowiedniego terminu jako takiego , ale trzeba jakoś wyjaśnić, które znaczenie jest używane za każdym razem. Nie robienie tego jest dokładnie tym, co powoduje zamieszanie.
źródło
Jest to sposób przekazywania argumentów do funkcji. Przekazywanie przez referencję oznacza, że parametr wywoływanych funkcji będzie taki sam, jak argument przekazanego argumentu (nie wartość, ale tożsamość - sama zmienna). Przekaż przez wartość oznacza, że parametr wywoływanych funkcji będzie kopią przekazanego argumentu wywołujących. Wartość będzie taka sama, ale tożsamość - zmienna - jest inna. Zatem zmiany parametru dokonanego przez wywoływaną funkcję w jednym przypadku zmieniają przekazany argument, aw drugim przypadku po prostu zmieniają wartość parametru w wywoływanej funkcji (która jest tylko kopią). W pośpiechu:
ref
używane w funkcji wywołującej i wywoływanej). Jon Skeet posiada również piękny wyjaśnienie tego tutaj .Kody
Ponieważ moim językiem jest C ++, użyję go tutaj
A przykład w Javie nie zaszkodzi:
Wikipedia
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference
Ten facet prawie to przybija:
http://javadude.com/articles/passbyvalue.htm
źródło
Wiele odpowiedzi tutaj (a zwłaszcza najbardziej wysoko ocenionych) jest niepoprawnych pod względem faktycznym, ponieważ źle rozumieją, co tak naprawdę oznacza „wywołanie przez odniesienie”. Oto moja próba uporządkowania spraw.
TL; DR
Mówiąc najprościej:
W kategoriach metaforycznych:
Co nie znaczy „call by value” i „call by reference”
Zauważ, że obie te koncepcje są całkowicie niezależne i ortogonalne od koncepcji typów referencyjnych (która w Javie jest wszystkimi typami, które są podtypami
Object
, a w C # wszystkieclass
typy), lub koncepcji typów wskaźników jak w C (które są semantycznie równoważne na „typy referencyjne” Javy, po prostu z inną składnią).Pojęcie typu odwołania odpowiada adresowi URL: zarówno on sam, jak i informacja, i jest odniesieniem ( wskaźnikiem , jeśli wolisz) do innych informacji. Możesz mieć wiele kopii adresu URL w różnych miejscach i nie zmieniają one witryny, do której prowadzą wszystkie linki; jeśli witryna zostanie zaktualizowana, każda kopia adresu URL będzie nadal prowadzić do zaktualizowanych informacji. I odwrotnie, zmiana adresu URL w jednym miejscu nie wpłynie na żadną inną pisemną kopię adresu URL.
Zauważ, że C ++ ma pojęcie „referencje” (np.
int&
), Które nie jest podobne do „typów referencji” Java i C #, ale jest jak „wywołanie przez referencję”. „Typy referencyjne” Java i C # oraz wszystkie typy w Pythonie przypominają to, co C i C ++ nazywają „typami wskaźników” (npint*
.).OK, oto dłuższe i bardziej formalne wyjaśnienie.
Terminologia
Na początek chcę podkreślić kilka ważnych fragmentów terminologii, aby pomóc wyjaśnić moją odpowiedź i upewnić się, że wszyscy odwołujemy się do tych samych pomysłów, gdy używamy słów. (W praktyce uważam, że zdecydowana większość nieporozumień związanych z takimi tematami wynika z używania słów w sposób, który nie do końca przekazuje zamierzone znaczenie).
Na początek przykład w deklaracji funkcji w języku C podobnym do C:
A oto przykład wywołania tej funkcji:
Korzystając z tego przykładu, chcę zdefiniować kilka ważnych fragmentów terminologii:
foo
jest funkcją zadeklarowaną w wierszu 1 (Java nalega na stworzenie metod wszystkich funkcji, ale koncepcja jest taka sama bez utraty ogólności; C i C ++ wprowadzają rozróżnienie między deklaracją a definicją, do której nie będę tutaj wchodził)param
to parametr formalny dofoo
zadeklarował również na linii 1arg
jest zmienną , w szczególności lokalną zmienną funkcjibar
, zadeklarowaną i zainicjowaną w wierszu 2arg
jest również argumentem do konkretnego wywołania zfoo
na linii 3Istnieją dwa bardzo ważne zestawy pojęć do rozróżnienia. Pierwszy to wartość w porównaniu ze zmienną :
bar
powyższej funkcji po wierszuint arg = 1;
wyrażeniearg
ma wartość1
.final
lub C #readonly
) lub głęboko niezmienna (np. Przy użyciu C ++const
).Inną ważną parą pojęć do rozróżnienia jest parametr kontra argument :
Zadzwoń według wartości
W wywołaniu według wartości formalne parametry funkcji są zmiennymi, które są nowo tworzone dla wywołania funkcji i które są inicjowane wartościami ich argumentów.
Działa to dokładnie tak samo, jak wszelkie inne rodzaje zmiennych są inicjowane wartościami. Na przykład:
Tutaj
arg
ianother_variable
są całkowicie niezależnymi zmiennymi - ich wartości mogą się zmieniać niezależnie od siebie. Jednak w punkcie, w którymanother_variable
jest zadeklarowane, jest on inicjowany, aby przechowywał tę samą wartość, któraarg
posiada - czyli jest1
.Ponieważ są to zmienne niezależne, zmiany,
another_variable
które nie mają wpływu naarg
:Jest to dokładnie to samo, co związek między
arg
iparam
w naszym powyższym przykładzie, który powtórzę tutaj dla symetrii:Jest tak, jakbyśmy napisali kod w ten sposób:
Oznacza to, że cechą charakterystyczną tego, co oznacza wywołanie przez wartość , jest to, że odbiorca (
foo
w tym przypadku) otrzymuje wartości jako argumenty, ale ma własne oddzielne zmienne dla tych wartości ze zmiennych wywołującego (bar
w tym przypadku).Wracając do mojej metafory powyżej, jeśli jestem
bar
, a tyfoo
, gdy wzywam was, oddam ci kawałek papieru z wartością na nim napisane. Nazywasz ten kawałek papieruparam
. Ta wartość jest kopią wartości, którą zapisałem w zeszycie (moje zmienne lokalne) w zmiennej, którą wywołujęarg
.(Nawiasem mówiąc : w zależności od sprzętu i systemu operacyjnego istnieją różne konwencje wywoływania dotyczące sposobu wywoływania jednej funkcji z drugiej. Konwencja wywoływania przypomina naszą decyzję, czy zapisać wartość na kartce papieru, a następnie przekazać ją tobie , lub jeśli masz kawałek papieru, na którym piszę, lub jeśli piszę go na ścianie przed nami obojgiem. To także interesujący temat, ale daleko poza zakresem tej i tak już długiej odpowiedzi.)
Zadzwoń przez referencję
W wywołaniu przez odniesienie parametry formalne funkcji są po prostu nowymi nazwami dla tych samych zmiennych, które wywołujący podaje jako argumenty.
Wracając do naszego przykładu powyżej, jest to równoważne z:
Ponieważ
param
jest to po prostu inna nazwaarg
- to znaczy, że są tą samą zmienną , w której zmianyparam
są odzwierciedlonearg
. Jest to podstawowy sposób, w jaki połączenie przez odniesienie różni się od połączenia według wartości.Bardzo niewiele języków obsługuje wywołanie przez odniesienie, ale C ++ może to zrobić w następujący sposób:
W tym przypadku,
param
nie tylko mają taką samą wartość , jakarg
to faktycznie jestarg
(tylko pod inną nazwą), a więcbar
można zauważyć, żearg
została zwiększona.Zauważ, że nie działa to w żaden sposób z Java, JavaScript, C, Objective-C, Python ani prawie żadnego innego popularnego języka. Oznacza to, że te języki nie są wywoływane przez odniesienie, są wywoływane według wartości.
Dodatek: połączenie przez udostępnianie obiektów
Jeśli masz wywołanie według wartości , ale rzeczywista wartość jest typem referencyjnym lub wskaźnikiem , to sama „wartość” nie jest zbyt interesująca (np. W C jest to liczba całkowita o rozmiarze specyficznym dla platformy) - co to jest ciekawe jest to, na co wskazuje ta wartość .
Jeśli to, na co wskazuje ten typ odniesienia (czyli wskaźnik), jest zmienne, możliwy jest interesujący efekt: możesz zmodyfikować wartość wskazywaną, a dzwoniący może obserwować zmiany wartości wskazywanej, nawet jeśli dzwoniący nie może obserwować zmiany w samym wskaźniku.
Aby ponownie pożyczyć analogię adresu URL, fakt, że dałem ci kopię adresu URL do strony internetowej, nie jest szczególnie interesujący, jeśli chodzi o stronę, a nie o adres URL. Fakt, że zapisujesz swoją kopię adresu URL, nie ma wpływu na moją kopię adresu URL, nie jest to dla nas ważne (w rzeczywistości w językach takich jak Java i Python „URL” lub wartość typu odwołania może wcale nie będzie modyfikowany, tylko to, na co wskaże, może).
Barbara Liskov, gdy wynalazła język programowania CLU (który miał taką semantykę), zdała sobie sprawę, że istniejące terminy „call by value” i „call by reference” nie były szczególnie przydatne do opisu semantyki tego nowego języka. Wymyśliła więc nowy termin: połączenie przez udostępnianie obiektów .
Podczas omawiania języków, które technicznie nazywane są wartością, ale tam, gdzie często używane są typy referencyjne lub wskaźnikowe (to znaczy: prawie każdy współczesny język programowania imperatywny, obiektowy lub oparty na wielu paradygmatach), uważam, że jest to mniej mylące po prostu unikaj mówienia o połączeniu według wartości lub połączeniu przez odniesienie . Trzymaj się połączenia przez udostępnianie obiektu (lub po prostu zadzwoń przez obiekt ) i nikt się nie pomyli. :-)
źródło
The first is value versus variable.
The other important pair of concepts to distinguish is parameter versus argument:
Przed zrozumieniem 2 warunków MUSISZ zrozumieć następujące. Każdy obiekt ma 2 rzeczy, dzięki którym można go rozróżnić.
Więc jeśli powiesz
employee.name = "John"
wiedz, że są 2 rzeczy
name
. Jej wartość, która jest"John"
i również jego położenie w pamięci, który jest jakiś numer szesnastkowy może tak:0x7fd5d258dd00
.W zależności od architektury języka lub typu (klasy, struktury itp.) Twojego obiektu, możesz przenieść
"John"
lub0x7fd5d258dd00
Przekazywanie
"John"
jest znane jako przekazywanie według wartości. Przekazywanie0x7fd5d258dd00
jest znane jako przekazywanie przez odniesienie. Każdy, kto wskazuje na tę lokalizację pamięci, będzie miał dostęp do wartości"John"
.Aby uzyskać więcej informacji na ten temat, polecam przeczytać o dereferencjowaniu wskaźnika i dowiedzieć się, dlaczego wybrać struct (typ wartości) zamiast klasy (typ odwołania)
źródło
Oto przykład:
źródło
y
został już ustawiony na 2 w poprzednim wierszu. Dlaczego miałoby wrócić do 0?Najprostszym sposobem na uzyskanie tego jest plik Excel. Powiedzmy na przykład, że masz dwie liczby, 5 i 2 odpowiednio w komórkach A1 i B1, i chcesz znaleźć ich sumę w trzeciej komórce, powiedzmy A2. Możesz to zrobić na dwa sposoby.
Albo przekazując ich wartości do komórki A2 , wpisując = 5 + 2 w tej komórce. W takim przypadku, jeśli zmienią się wartości komórek A1 lub B1, suma w A2 pozostaje taka sama.
Lub przekazując „referencje” komórek A1 i B1 do komórki A2 , wpisując = A1 + B1 . W takim przypadku, jeśli zmienią się wartości komórek A1 lub B1, zmieni się również suma w A2.
źródło
Przechodząc przez ref, zasadniczo przekazujesz wskaźnik do zmiennej. Przekaż według wartości przekazujesz kopię zmiennej. W podstawowym użyciu zwykle oznacza to, że zmiany pass by ref w zmiennej będą widoczne jako metoda wywołująca i pass by value, których nie chcą.
źródło
Przekaż przez wartość wysyła KOPIA danych przechowywanych w określonej zmiennej, przekazanie przez odniesienie wysyła bezpośredni link do samej zmiennej. Więc jeśli przekażesz zmienną przez odniesienie, a następnie zmienisz zmienną w bloku, do którego ją przekazałeś, zmienna oryginalna zostanie zmieniona. Jeśli po prostu przekażesz wartość, pierwotna zmienna nie będzie mogła zostać zmieniona przez blok, w którym ją przekazałeś, ale dostaniesz kopię tego, co zawierała w momencie wywołania.
źródło
Przekaż według wartości - funkcja kopiuje zmienną i działa z kopią (więc nie zmienia niczego w oryginalnej zmiennej)
Przekaż przez referencję - funkcja używa oryginalnej zmiennej, jeśli zmienisz zmienną w innej funkcji, zmieni się ona również w oryginalnej zmiennej.
Przykład (skopiuj i użyj / wypróbuj to sam i zobacz):
Prosto, zerkaj. Ściany tekstu mogą być złym nawykiem.
źródło
Główną różnicą między nimi jest to, że zmienne typu wartości przechowują wartości, więc określenie zmiennej typu wartości w wywołaniu metody przekazuje kopię wartości tej zmiennej do metody. Zmienne typu odniesienia przechowują odniesienia do obiektów, więc określenie zmiennej typu odwołania jako argumentu przekazuje metodzie kopię faktycznego odwołania, które odnosi się do obiektu. Mimo że sama referencja jest przekazywana przez wartość, metoda może nadal używać referencji, którą otrzymuje, do interakcji z oryginalnym obiektem - i ewentualnie do modyfikacji. Podobnie, gdy zwracane są informacje z metody za pomocą instrukcji return, metoda zwraca kopię wartości przechowywanej w zmiennej typu wartości lub kopię odwołania przechowywaną w zmiennej typu odwołania. Po zwróceniu odwołania metoda wywołująca może użyć tego odwołania do interakcji z obiektem, do którego się odwołuje. Więc,
W c #, aby przekazać zmienną przez referencję, tak zwana metoda może modyfikować zmienną, C # zapewnia słowa kluczowe ref i out. Zastosowanie słowa kluczowego ref do deklaracji parametru pozwala przekazać zmienną do metody przez referencję - wywoływana metoda będzie mogła zmodyfikować oryginalną zmienną w wywołującym. Słowo kluczowe ref służy do zmiennych, które zostały już zainicjowane w metodzie wywoływania. Zwykle, gdy wywołanie metody zawiera niezainicjowaną zmienną jako argument, kompilator generuje błąd. Poprzedzenie parametru bez słowa kluczowego tworzy parametr wyjściowy. Wskazuje to kompilatorowi, że argument zostanie przekazany do wywoływanej metody przez odniesienie i że wywoływana metoda przypisze wartość oryginalnej zmiennej w wywołującym. Jeśli metoda nie przypisuje wartości do parametru wyjściowego w każdej możliwej ścieżce wykonania, kompilator generuje błąd. Zapobiega to również generowaniu przez kompilator komunikatu o błędzie dla niezainicjowanej zmiennej, która jest przekazywana jako argument do metody. Metoda może zwrócić tylko jedną wartość do swojego obiektu wywołującego za pomocą instrukcji return, ale może zwrócić wiele wartości poprzez określenie wielu parametrów wyjściowych (ref i / lub out).
patrz dyskusja c # i przykłady tutaj link tekst
źródło
Przykłady:
const &
jest ogólnie najlepszy. Nie ponosisz kary za budowę i zniszczenie. Jeśli odniesienie nie jest stałe, interfejs sugeruje, że zmieni przekazane dane.źródło
Krótko mówiąc, wartość przekazywana to CO TO jest, a wartość referencyjna to GDZIE to jest.
Jeśli twoja wartość to VAR1 = „Happy Guy!”, Zobaczysz tylko „Happy Guy!”. Jeśli VAR1 zmieni się na „Happy Gal!”, Nie będziesz tego wiedział. Jeśli zostanie przekazany przez odniesienie, a VAR1 zmieni się, to zrobisz.
źródło
Jeśli nie chcesz zmieniać wartości oryginalnej zmiennej po przekazaniu jej do funkcji, funkcja powinna zostać zbudowana z parametrem „ pass by value ”.
Wtedy funkcja będzie miała TYLKO wartość, ale nie adres przekazywanej zmiennej. Bez adresu zmiennej kod wewnątrz funkcji nie może zmienić wartości zmiennej widzianej z zewnątrz funkcji.
Ale jeśli chcesz dać tej funkcji możliwość zmiany wartości zmiennej widzianej z zewnątrz, musisz użyć pass by reference . Ponieważ zarówno wartość, jak i adres (odniesienie) są przekazywane i dostępne wewnątrz funkcji.
źródło
pass by value oznacza, jak przekazać wartość do funkcji przy użyciu argumentów. przekazując wartość kopiujemy dane przechowywane w określonej przez nas zmiennej i jest ona wolniejsza niż przekazywanie przez referencję, ponieważ dane są kopiowane. z dokonanych zmian w skopiowanych danych nie wpłynie to na oryginalne dane. W drodze referencji lub adresu wysyłamy bezpośredni link do samej zmiennej. lub przekazanie wskaźnika do zmiennej. jest to szybsze, ponieważ zużywa mniej czasu
źródło
Oto przykład, który pokazuje różnice między przekazywaniem wartości - wartością wskaźnika - referencją :
Metoda „przejścia przez odniesienie” ma ważne ograniczenie . Jeśli parametr jest zadeklarowany jako przekazany przez referencję (więc jest poprzedzony znakiem &), odpowiadający mu parametr rzeczywisty musi być zmienną .
Rzeczywisty parametr odnoszący się do parametru formalnego „przekazany przez wartość” może być ogólnie wyrażeniem , więc można używać nie tylko zmiennej, ale również wyniku wywołania funkcji dosłownej lub nawet funkcji.
Funkcja nie może umieścić wartości w czymś innym niż zmienna. Nie może przypisać nowej wartości do literału ani zmusić wyrażenia do zmiany wyniku.
PS: Możesz także sprawdzić odpowiedź Dylana Beattie w bieżącym wątku, który wyjaśnia to prostymi słowami.
źródło