Przechodzić przez odniesienie lub przejść przez wartość?

głosy
48

Podczas uczenia się nowego języka programowania, jedną z możliwych blokad dróg, które można napotkać jest pytanie, czy język jest domyślnie przejść przez wartość lub przekazywany przez referencję .

Więc tutaj jest moje pytanie do was wszystkich, w swoim ulubionym języku, jak jest to rzeczywiście zrobić? I jakie są ewentualne pułapki ?

Twój ulubiony język może, oczywiście, być cokolwiek kiedykolwiek grał z: popularny , niejasne , ezoteryczny , nowy , stary ...

Utwórz 05/08/2008 o 09:56
źródło użytkownik
W innych językach...                            


11 odpowiedzi

głosy
31

Tu jest mój własny wkład do języka programowania Java .

Pierwsza część kodu:

public void swap(int x, int y)
{
  int tmp = x;
  x = y;
  y = tmp;
}

wywołanie tej metody spowoduje to:

int pi = 3;
int everything = 42;

swap(pi, everything);

System.out.println("pi: " + pi);
System.out.println("everything: " + everything);

"Output:
pi: 3
everything: 42"

nawet przy użyciu „realne” obiekty pokaże podobny wynik:

public class MyObj {
    private String msg;
    private int number;

    //getters and setters
    public String getMsg() {
        return this.msg;
    }


    public void setMsg(String msg) {
        this.msg = msg;
    }


    public int getNumber() {
        return this.number;
    }


    public void setNumber(int number) {
        this.number = number;
    }

    //constructor
    public MyObj(String msg, int number) {
        setMsg(msg);
        setNumber(number);
    }
}

public static void swap(MyObj x, MyObj y)
{
    MyObj tmp = x;
    x = y;
    y = tmp;
}

public static void main(String args[]) {
    MyObj x = new MyObj("Hello world", 1);
    MyObj y = new MyObj("Goodbye Cruel World", -1); 

    swap(x, y);

    System.out.println(x.getMsg() + " -- "+  x.getNumber());
    System.out.println(y.getMsg() + " -- "+  y.getNumber());
}


"Output:
Hello world -- 1
Goodbye Cruel World -- -1"

zatem oczywiste jest, że Java przekazuje swoje parametry przez wartość , jako wartość PI i wszystkiego , a obiekty myObj nie są zamienione. zdawać sobie sprawę, że „wartością” jest jedynym sposobem w Javie przekazać parametry do metody. (na przykład język C ++ umożliwia wywoływacza przekazać parametr odniesienia przy zastosowaniu „ i ” po typu parametru)

Teraz skomplikowana część , a przynajmniej ta część, która będzie mylić większość nowych programistów Java: (zapożyczone z javaworld )
Original autor: Tony Sintes

public void tricky(Point arg1, Point arg2)
{
    arg1.x = 100;
    arg1.y = 100;
    Point temp = arg1;
    arg1 = arg2;
    arg2 = temp;
}
public static void main(String [] args)
{
    Point pnt1 = new Point(0,0);
    Point pnt2 = new Point(0,0);
    System.out.println("X: " + pnt1.x + " Y: " +pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
    System.out.println(" ");
    tricky(pnt1,pnt2);
    System.out.println("X: " + pnt1.x + " Y:" + pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);  
}


"Output
X: 0 Y: 0
X: 0 Y: 0
X: 100 Y: 100
X: 0 Y: 0"

trudne skutecznie zmienia wartość pnt1! Oznaczałoby to, że obiekty są przekazywane przez referencję, to nie jest przypadek! Prawidłowe zestawienie byłoby: te referencje obiektów są przekazywane przez wartość.

więcej od Tony Sintes:

Metoda z powodzeniem zmienia wartość pnt1, choć jest on przekazywany przez wartość; Jednak zamiana pnt1 i PNT2 zawiedzie! To jest głównym źródłem nieporozumień. W głównej () metoda, pnt1 i PNT2 są niczym więcej niż odniesień do obiektu. Po przejechaniu pnt1 i PNT2 metody trudne (), Java przechodzi przez wartość odniesienia, tak jak każdy inny parametr. Oznacza to, że referencje przekazywane do metody są rzeczywiście kopie oryginalnych referencje. Figura 1 pokazuje dwa odniesienia wskazujące na tym samym obiekcie po Java przechodzi obiekt do metody.

Figura 1 http://www.javaworld.com/javaworld/javaqa/2000-05/images/03-qa-0512-pass2b.gif

Wnioski lub długie opowiadanie:

  • Parametry Java przechodzi ona przez wartość
  • „wartością” jest jedynym sposobem w Java przekazać parametr sposobu
  • za pomocą metody z obiektu podanego jako parametr zmieni obiekt jako punkt odniesienia do oryginalnych obiektów. (jeśli sam sposób modyfikuje niektóre wartości)

Przydatne linki:

Odpowiedział 05/08/2008 o 09:56
źródło użytkownik

głosy
20

Oto kolejny artykuł w języku programowania C #

c # przechodzi swoich argumentów przez wartość (domyślnie)

private void swap(string a, string b) {
  string tmp = a;
  a = b;
  b = tmp;
}

wywołanie tej wersji zamiany będzie miał zatem żadnego rezultatu:

string x = "foo";
string y = "bar";
swap(x, y);

"output: 
x: foo
y: bar"

Jednak w odróżnieniu od java c # nie dać developer możliwość przekazywania parametrów przez odniesienie , odbywa się to za pomocą słowa kluczowego „ref” przed typu parametru:

private void swap(ref string a, ref string b) {
  string tmp = a;
  a = b;
  b = tmp;
} 

Ta zamiana będzie zmienić wartość parametru odwołanie:

string x = "foo";
string y = "bar";
swap(x, y);

"output: 
x: bar
y: foo"

C # posiada również wyjścia słowa kluczowego , a różnica między ref i out jest subtelna. z MSDN:

Dzwoniący metody, która zajmuje się parametr nie jest wymagany, aby przypisać do zmiennej przekazanej jako parametr wyjściowy przed wywołaniem; Jednak wywoływany jest wymagane, aby przypisać do parametru out przed powrotem.

i

W przeciwieństwie parametry refuważane za początkowo przydzielony przez odbierającym. Jako taki, wywoływany jest nie wymagany przypisać do ref parametru przed użyciem. Ref parametry przekazywane są zarówno do i od metody.

mała pułapka, podobnie jak w Javie, że obiekty przekazywane przez wartość nadal można zmieniać za pomocą ich wewnętrzne metody

wniosek:

  • c # przekazuje parametry domyślnie wartością
  • ale w razie potrzeby parametry mogą być także przekazywane przez odniesienie za pomocą słowa kluczowego ref
  • Wewnętrzne metody z parametrem przechodzi przez wartość zmieni przedmiotu (jeśli sam sposób modyfikuje niektóre wartości)

Przydatne linki:

Odpowiedział 05/08/2008 o 17:40
źródło użytkownik

głosy
19

Python używa przekazywany przez wartość, ale ponieważ wszystkie te wartości są referencje obiektów, efekt netto jest coś podobnego do pass-by-reference. Jednak programiści Python myśleć o tym, czy typ obiektu jest zmienne lub niezmienne . Zmienne obiekty mogą zostać zmienione w miejscu (na przykład słowniki, listy zdefiniowanych przez użytkownika obiektów), natomiast niezmienne obiekty mogą też nie (np liczb całkowitych, smyczki, krotki).

Poniższy przykład ilustruje funkcję, która jest przekazywana dwa argumenty, jest niezmienny ciąg, a także listę zmienny.

>>> def do_something(a, b):
...     a = "Red"
...     b.append("Blue")
... 
>>> a = "Yellow"
>>> b = ["Black", "Burgundy"]
>>> do_something(a, b)
>>> print a, b
Yellow ['Black', 'Burgundy', 'Blue']

Linia a = "Red"jedynie tworzy lokalną nazwę a, dla wartości ciąg "Red"i ma wpływ na przekazywane w argumencie (co jest ukryte, jak anależy odnosić się do lokalnej nazwy od tej pory). Cesja nie jest praca na miejscu, niezależnie od tego, czy argument jest zmienne lub niezmienne.

bParametr jest odwołanie do obiektu listy zmienny, a .append()metoda wykonuje rozszerzenie w miejscu na liście, sklejaniu na nową "Blue"wartość ciągu.

(Ponieważ obiekty String są niezmienne, nie ma żadnych metod, które wspierają w miejscu modyfikacje).

Po powrocie funkcji, ponowne przypisanie amiał żadnego wpływu, natomiast rozszerzenie bwyraźnie pokazuje pass-by-reference semantyki wywołania styl.

Jak wspomniano wcześniej, nawet jeżeli argumentem ajest rodzajem zmienny, ponowne przypisanie wewnątrz funkcji nie jest praca na miejscu, a więc nie będzie zmiana wartości przekazany argumentu:

>>> a = ["Purple", "Violet"]
>>> do_something(a, b)
>>> print a, b
['Purple', 'Violet'] ['Black', 'Burgundy', 'Blue', 'Blue']

Jeśli nie chcesz, aby listy zmodyfikowany przez wywołanej funkcji, byś zamiast używać niezmienny typ krotny (wskazanym przez nawiasach w formie dosłownej, zamiast nawiasów kwadratowych), która nie obsługuje w miejscu .append()metodę:

>>> a = "Yellow"
>>> b = ("Black", "Burgundy")
>>> do_something(a, b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in do_something
AttributeError: 'tuple' object has no attribute 'append'
Odpowiedział 23/08/2008 o 17:50
źródło użytkownik

głosy
7

Ponieważ nie widziałem jeszcze odpowiedzi Perl, pomyślałem, że napisać.

Pod maską, Perl działa skutecznie, jak przechodzić przez odwołanie. Zmienne jako argumenty wywołania funkcji są przekazywane referentially, stałe są przekazywane jako tylko do odczytu wartości, a wyniki są przekazywane jako wyrazu tymczasowych. Zwykłe idiomy skonstruować listy argumentów przez przypisanie listy Z @_, lub shiftmają tendencję do ukrywania tego od użytkownika, dając wygląd pass-by-value:

sub incr {
  my ( $x ) = @_;
  $x++;
}

my $value = 1;
incr($value);
say "Value is now $value";

Będzie to wydrukować Value is now 1, ponieważ $x++jest zwiększany zmienną zadeklarowaną w ramach leksykalneincr() funkcji, zamiast zmiennej przekazany w. Ten pass-by-value styl jest zwykle to, co jest poszukiwany przez większość czasu, a funkcje, które modyfikują swoje argumenty są rzadkością w Perlu, a styl należy unikać.

Jednakże, jeśli z jakiegoś powodu, to zachowanie jest szczególnie pożądane, można osiągnąć przez działanie bezpośrednio na elementach @_tablicy, ponieważ będą one aliasami zmiennych przekazywane do funkcji.

sub incr {
  $_[0]++;
}

my $value = 1;
incr($value);
say "Value is now $value";

Tym razem będzie drukować Value is now 2, ponieważ $_[0]++ekspresja zwiększa się rzeczywistą $valuezmiennej. Sposób, w jaki to działa jest to, że pod maską @_nie jest prawdziwa tablica podobnie jak większość innych tablic (jak można by uzyskać za my @array), ale zamiast jego elementy są wbudowane bezpośrednio z argumentów przekazywanych do wywołania funkcji. To pozwala skonstruować przekazywany przez odniesienie semantykę jeśli to będzie konieczne. Argumenty wywołania funkcji, które są zmienne ślizgowe są wstawiane jak jest w tej tablicy, a stałymi lub wyniki z bardziej skomplikowanych wyrażeń są wstawiane jako tylko do odczytu tymczasowych.

Jest to jednak niezmiernie rzadko to zrobić w praktyce, ponieważ Perl obsługuje wartości referencyjnych; czyli wartości, które odnoszą się do innych zmiennych. Zwykle jest o wiele jaśniejszy skonstruowanie funkcji, która jest oczywiste efekty uboczne o zmiennej przepuszczając w odniesieniu do tej zmiennej. Jest to wyraźne wskazanie do czytnika w callsite, że semantyka przejazdu porównawcze są aktywne.

sub incr_ref {
  my ( $ref ) = @_;
  $$ref++;
}

my $value = 1;
incr(\$value);
say "Value is now $value";

Tutaj \operator uzyskuje odwołanie w taki sam sposób jak w przypadku &adresu operatora w C.

Odpowiedział 13/04/2012 o 16:33
źródło użytkownik

głosy
6

Jest to dobre wyjaśnienie tutaj dla .NET.

Wiele osób jest niespodzianką, że obiekty referencyjne są rzeczywiście przekazywane przez wartość (zarówno w C # i Java). Jest to kopia adres stosu. Zapobiega to metoda zmieniania gdzie obiekt rzeczywiście wskazuje, ale nadal pozwala to metoda polegająca na zmianie wartości obiektu. W języku C # można przekazać jego odwołanie przez referencję, co oznacza, że ​​można zmienić gdzie an rzeczywiste punkty sprzeciw wobec.

Odpowiedział 06/08/2008 o 23:43
źródło użytkownik

głosy
5

Nie zapomnij Istnieje również przejść przez nazwę i przejść przez wartość rezultatu .

Przechodzą przez wartość rezultatu jest podobne do przejść przez wartość, z dodatkowym aspektem, że stosunek mieści się w pierwotnej zmiennej, która została przyjęta jako parametr. To może w pewnym stopniu uniknąć interferencji z zmiennych globalnych. Jest widocznie lepiej partycje pamięci, gdzie przechodzą przez odniesienie może spowodować usterkę strony ( Reference ).

Pass od nazwy oznacza, że wartości są obliczane tylko wtedy, gdy są one rzeczywiście używane, zamiast na początku procedury. Algol wykorzystywane pass-by-name, ale ciekawym efektem ubocznym jest to, że jest to bardzo trudne, aby napisać procedurę wymiany ( Reference ). Ponadto, ekspresja zapadają nazwy jest ponownej ocenie za każdym razem, gdy jest dostępne, co również może powodować działania niepożądane.

Odpowiedział 05/08/2008 o 11:00
źródło użytkownik

głosy
4

Cokolwiek powiesz jak przejść przez wartość lub przechodzić przez odwołanie musi być spójne we wszystkich językach. Najczęstszym i spójna definicja używana w różnych językach jest to, że przy przejściu przez referencję, można przekazać zmienną do funkcji „normalnie” (czyli bez wyraźnego biorąc adres lub coś podobnego), a funkcja może przydzielić (nie mutują zawartość) parametr wewnątrz funkcji i będzie mieć taki sam skutek jak przypisanie do zmiennej w zakresie dzwonisz.

Z tego punktu widzenia, języki są pogrupowane w następujący sposób; każda grupa o tej samej semantyki mijania. Jeśli uważasz, że dwa języki nie powinny być umieszczone w tej samej grupie, Wzywam Was wymyślić przykład, który je odróżnia.

Zdecydowana większość języków, w tym C , Java , Python , Ruby , JavaScript , Scheme , OCaml , standardowa ML , Go , Objective-C , Smalltalk , itd. To wszystko przechodzić przez wartość tylko . Przekazując wartość wskaźnika (niektóre języki nazwać „odniesienia”) nie liczy się jako mijają odniesienia; jesteśmy tylko o rzeczy przeszły, wskaźnik, a nie rzeczą wskazał.

Języki takie jak C ++ , C # , PHP są domyślnie przejść przez wartość jak wyżej językach, ale funkcje mogą jawnie zadeklarować parametry być przekazywany przez referencję, za pomocą &lub ref.

Perl zawsze przechodzą przez odwołanie; Jednak w praktyce ludzie prawie zawsze skopiować wartości po otrzymaniu go, więc go używać w sposób przekazywany przez wartość.

Odpowiedział 13/04/2012 o 21:00
źródło użytkownik

głosy
4

o wartości

  • jest niższa niż odniesienie, ponieważ system musi skopiować parametr
  • wykorzystywane wyłącznie dla wejścia

przez odniesienie

  • szybciej, ponieważ tylko wskaźnik jest przekazywana
  • wykorzystywane do wejścia i wyjścia
  • może być bardzo niebezpieczne, jeśli są stosowane w połączeniu ze zmiennych globalnych
Odpowiedział 05/08/2008 o 10:10
źródło użytkownik

głosy
3

Odnośnie J , a tam jest tylko, AFAIK, przechodzącej przez wartość, nie jest formą przechodzącej przez odniesienie, które umożliwia przesuwanie dużo danych. Wystarczy przekazać coś znanego jako lokum dla czasownika (lub funkcja). To może być instancją klasy lub po prostu ogólny pojemnik.

spaceused=: [: 7!:5 <
exectime =: 6!:2
big_chunk_of_data =. i. 1000 1000 100
passbyvalue =: 3 : 0
    $ y
    ''
)
locale =. cocreate''
big_chunk_of_data__locale =. big_chunk_of_data
passbyreference =: 3 : 0
    l =. y
    $ big_chunk_of_data__l
    ''
)
exectime 'passbyvalue big_chunk_of_data'
   0.00205586720663967
exectime 'passbyreference locale'
   8.57957102144893e_6

Oczywistą wadą jest to, że trzeba znać nazwę zmiennej w jakiś sposób w nazwie funkcji. Ale ta technika może poruszać się dużo danych bezboleśnie. Dlatego, gdy technicznie nie przechodzą przez odniesienie, ja to nazywam „prawie że”.

Odpowiedział 21/11/2009 o 18:14
źródło użytkownik

głosy
2

PHP jest również przechodzić przez wartość.

<?php
class Holder {
    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }
}

function swap($x, $y) {
    $tmp = $x;
    $x = $y;
    $y = $tmp;
}

$a = new Holder('a');
$b = new Holder('b');
swap($a, $b);

echo $a->getValue() . ", " . $b->getValue() . "\n";

wyjścia:

a b

Jednak w PHP4 obiekty były traktowane jak prymitywów . Co znaczy:

<?php
$myData = new Holder('this should be replaced');

function replaceWithGreeting($holder) {
    $myData->setValue('hello');
}

replaceWithGreeting($myData);
echo $myData->getValue(); // Prints out "this should be replaced"
Odpowiedział 11/08/2008 o 03:21
źródło użytkownik

głosy
-1

Domyślnie ANSI / ISO C używa albo - to zależy od tego, w jaki sposób zadeklarować swoją funkcję i jego parametry.

Jeśli zadeklarować parametry funkcji jako wskaźniki wówczas funkcja będzie przechodzić przez odwołanie, a jeśli zadeklarować jako zmienne parametry funkcji nie-pointer to funkcja będzie przechodzić przez wartość.

void swap(int *x, int *y);   //< Declared as pass-by-reference.
void swap(int x, int y);     //< Declared as pass-by-value (and probably doesn't do anything useful.)

Można napotkasz problemy, jeśli utworzyć funkcję, która zwraca wskaźnik do zmiennej non-statycznego, który został utworzony w ramach tej funkcji. Zwrócona wartość następującego kodu będzie niezdefiniowana - nie ma sposobu, aby wiedzieć, czy pamięć przydzielona do tymczasowej zmiennej utworzonej w funkcji zostało nadpisane lub nie.

float *FtoC(float temp)
{
    float c;
    c = (temp-32)*9/5;
    return &c;
}

Można jednak zwrócić odwołanie do statycznej zmiennej lub wskaźnika, który został przekazany na liście parametrów.

float *FtoC(float *temp)
{
    *temp = (*temp-32)*9/5;
    return temp;
}
Odpowiedział 13/01/2011 o 21:48
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more