Passerer referanse eller passerer verdi?

stemmer
50

Når man lærer et nytt programmeringsspråk, en av de mulige veisperringer du kan støte på er spørsmålet om språket er, som standard, pass-by-verdi eller pass-by-referanse .

Så her er mitt spørsmål til dere alle, på din favoritt språket, hvordan er det egentlig gjøres? Og hva er de mulige fallgruvene ?

Din favoritt språk kan selvfølgelig være alt du noensinne har spilt med: populær , obskure , esoteriske , nye , gamle ...

Publisert på 05/08/2008 klokken 08:56
kilden bruker
På andre språk...                            


11 svar

stemmer
31

Her er mitt eget bidrag for Java programmeringsspråk .

først noen kode:

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

kalle denne metoden vil resultere i dette:

int pi = 3;
int everything = 42;

swap(pi, everything);

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

"Output:
pi: 3
everything: 42"

selv ved hjelp av 'ekte' objekter vil vise et lignende resultat:

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"

således er det klart at Java passerer dens parametere av verdi , da verdien for pi og alt , og de myObj objektene byttes ikke. være klar over at "av verdi" er den eneste måten i java å sende parametre til en metode. (for eksempel et språk som C ++ gjør det mulig for utvikleren å passere en parameter som referanse ved hjelp av ' og ' etter parametertypen)

nå den vanskelige delen , eller i det minste den delen som vil forvirre de fleste nye Java-utviklere: (lånt fra javaworld )
Opprinnelig forfatter: 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"

vanskelig vellykket endrer verdien av pnt1! Dette ville innebære at Objekter sendes som referanse, er dette ikke tilfelle! En riktig uttalelse vil være: de objektreferanser er vedtatt av verdi.

mer fra Tony Sintes:

Metoden med hell endrer verdien av pnt1, selv om den blir ført av verdi; men et bytte av pnt1 og PNT2 mislykkes! Dette er den viktigste kilden til forvirring. I hoved () -metoden, pnt1 og PNT2 er noe mer enn objekt referanser. Når du passerer pnt1 og PNT2 til vanskelig () -metoden, passerer Java referansene av verdi akkurat som alle andre parameter. Dette betyr at referanser sendes til metoden er faktisk kopier av original referanser. Figur 1 nedenfor viser to referanser som peker til det samme objektet etter Java passerer et objekt til en metode.

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

Konklusjon eller en lang historie kort:

  • Java passerer den parametere av verdien
  • "av verdi" er den eneste måten i java å passere en parameter til en metode
  • ved hjelp av metoder fra gjenstanden gis som parameter vil forandre den gjenstand som de referanser som viser til den opprinnelige stedene. (Hvis det metode i seg selv endrer noen verdier)

nyttige lenker:

Svarte 05/08/2008 kl. 08:56
kilden bruker

stemmer
20

Her er en annen artikkel for programmeringsspråket C #

c # passerer argumentene av verdi (som standard)

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

kalle denne versjonen av swap vil dermed ikke ha noen resultat:

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

"output: 
x: foo
y: bar"

Men i motsetning til java C # gjør gi utvikleren mulighet til å sende parametre ved henvisning , gjøres dette ved hjelp av 'ref' keyword før den type parameteren:

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

Dette swap vil endre verdien av den refererte parameter:

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

"output: 
x: bar
y: foo"

C # har også en ut søkeord , og forskjellen mellom dommeren og ut er en subtil en. fra MSDN:

Den som ringer av en metode som tar en ut parameter er ikke nødvendig å tilordne til variabelen som overføres som utparameter før samtalen; imidlertid, er anropt er nødvendig for å tilordne til utparameter før retur.

og

I motsetning ref parametere blir betraktet som er tilordnet innledningsvis ved anropt. Som sådan, er den anropte ikke nødvendig å tilordne til dommeren parameter før bruk. Ref parametere sendes både inn i og ut av en metode.

en liten fallgruve er, som i java, at gjenstander ble sendt av verdien fremdeles endres ved hjelp av sine indre metoder

konklusjon:

  • c # passerer dens parametere, som standard, ved verdien
  • men når nødvendige parametrene kan også sendes med henvisning hjelp dommeren søkeord
  • indre metoder fra en parameter passert av verdien vil forandre objektet (hvis denne metoden i seg selv endrer noen verdier)

nyttige lenker:

Svarte 05/08/2008 kl. 16:40
kilden bruker

stemmer
19

Python bruker pass-by-verdi, men siden alle slike verdier er objektreferanser, er nettoeffekten noe beslektet med pass-by-referanse. Men Python programmerere tenke mer på om en objekttype er foranderlig eller uforanderlig . Foranderlig objekter kan endres på stedet (f.eks, ordbøker, lister, brukerdefinerte objekter), mens uforanderlige objekter kan ikke (f.eks heltall, strykere, tupler).

Det følgende eksempel viser en funksjon som er gått to argumenter, en uforanderlig streng, og en foranderlig liste.

>>> 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']

Linjen a = "Red"skaper bare en lokal navn, afor strengverdi "Red"og har ingen effekt på den passerte inn argument (som nå er skjult, som askal referere til det lokale navn fra så videre). Oppgaven er ikke en erstatningsoperasjonen, uavhengig av om argumentet er foranderlig eller uforanderlig.

Den bparameteren er en referanse til et foranderlig objekt liste, og den .append()metode utfører en erstatnings forlengelse av listen, vinne på den nye "Blue"strengverdi.

(Fordi streng objekter er uforanderlig, har de ikke har noen metoder som støtter tilstedeværende modifikasjoner).

Når funksjonen returnerer, re-tildeling av ahar hatt noen effekt, mens utvidelse av btydelig viser pass-by-referansestil samtale semantikk.

Som nevnt før, selv om argumentet for aer en foranderlig type, er re-oppdrag innenfor funksjonen ikke en erstatnings drift, og slik at det skulle være noen endring i pass argument verdi:

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

Hvis du ikke vil at listen endres av kalt funksjon, ville du i stedet bruke den uforanderlige tuppel type (identifisert ved parentes i bokstavelig form, snarere enn hakeparenteser), som ikke støtter in-place .append()metode:

>>> 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'
Svarte 23/08/2008 kl. 16:50
kilden bruker

stemmer
7

Siden jeg ikke har sett en Perl svar ennå, jeg tenkte jeg skulle skrive en.

Under panseret, fungerer Perl effektivt som pass-by-referanse. Variabler som funksjon samtale argumentene er gått referentially, konstanter sendes som skrivebeskyttede verdier, og resultatene av uttrykk sendes som midlertidig materiale. De vanlige idiomer å konstruere argumentlister ved å liste oppdrag fra @_, eller ved shifttendens til å skjule dette fra brukeren, gir inntrykk av pass-by-verdi:

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

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

Dette vil skrive Value is now 1fordi $x++har økes den leksikalske variabel deklarert i incr()funksjon, snarere enn den variable gått i. Dette pass-by-verdi stil er vanligvis hva som er ønsket mesteparten av tiden, som funksjoner som endrer deres argumenter er sjelden i Perl, og stilen bør unngås.

Hvis imidlertid av en eller annen grunn denne oppførselen er spesielt ønskelig, kan det bli oppnådd ved å operere direkte på elementene i den @_oppstillingen, fordi de vil være aliaser for variabler gått inn i funksjonen.

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

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

Denne gangen vil skrive Value is now 2, fordi $_[0]++uttrykket økes selve $valuevariabel. Måten dette fungerer slik at under panseret @_er ikke en ekte matrise som de fleste andre rekker (som ville bli oppnådd ved my @array), men i stedet dens elementer er bygget direkte ut av de argumenter som sendes til et funksjonskall. Dette gjør det mulig å konstruere pass-by-referanse semantikk om det skulle være nødvendig. Funksjonskall argumenter som er vanlig variabler er innsatt som-er inn i denne matrisen, og konstanter eller resultatene av mer komplekse uttrykk settes inn som skrive kun midlertidig materiale.

Det er imidlertid svært sjelden å gjøre dette i praksis, fordi Perl støtter referanseverdier; det vil si verdier som refererer til andre variabler. Normalt er det langt tydeligere å konstruere en funksjon som har en opplagt bivirkning på en variabel, ved å føre inn en referanse til den aktuelle variabel. Dette er en klar indikasjon for leseren ved callsite, som kan bestå-av-referanse semantikk er i kraft.

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

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

Her \operatør gir en referanse på mye den samme måte som den &adresse-av operatør i C.

Svarte 13/04/2012 kl. 15:33
kilden bruker

stemmer
6

Det er en god forklaring her for .NET.

Mange mennesker er overraskende at referanseobjekter er faktisk vedtatt av verdi (i både C # og Java). Det er en kopi av en stabel adresse. Dette hindrer en metode i å endre hvor gjenstanden peker faktisk til, men likevel gir en metode for å endre verdiene av objektet. I C # det er mulig å passere en referanse som referanse, noe som betyr at du kan endre hvor en faktisk objekt peker til.

Svarte 06/08/2008 kl. 22:43
kilden bruker

stemmer
5

Ikke glem det er også passere ved navn , og passerer verdi resultat .

Passerer verdi-resultatet er lik passerer verdi, med den ekstra trekk at verdien er angitt i den opprinnelige variable som ble ført som parameter. Det kan til en viss grad, unngå interferens med globale variabler. Det er tydeligvis bedre i partisjonert minne, hvor et pass med referanse kan føre til en sidefeil ( Reference ).

Pass ved navn betyr at verdiene er bare beregnet når de faktisk blir brukt, snarere enn ved starten av prosedyren. Algol brukt pass-by-navn, men en interessant bieffekt er at det er veldig vanskelig å skrive en swap prosedyre ( Reference ). Også, uttrykket passerte navn er re-evaluert hver gang den er åpnet, noe som også kan ha bivirkninger.

Svarte 05/08/2008 kl. 10:00
kilden bruker

stemmer
4

Uansett hva du sier så pass-by-verdi eller pass-by-referansen må være konsekvent på tvers av språk. Den vanligste og konsistent definisjon som brukes på tvers av språk er at med pass-by-referanse, kan du passere en variabel til en funksjon "normalt" (dvs. uten eksplisitt tar adresse eller noe sånt), og funksjonen kan tildele (ikke mutere innholdet i) den parameteren inne i funksjon og vil ha den samme virkning som å tilordne til den variable i den anropende omfang.

Fra denne visningen, er de språkene gruppert som følger; hver gruppe har de samme passerende semantikk. Hvis du tror at to språk ikke bør settes i samme gruppe, jeg utfordrer deg til å komme opp med et eksempel som skiller dem.

De aller fleste språk, inkludert C , Java , Python , Ruby , Javascript , Scheme , Objective Caml , Standard ML , Go , Objective-C , Smalltalk , etc. er alle pass-by-verdi bare . Passerer en peker verdi (noen språk kaller det en "referanse") teller ikke som pasning fra referanse; vi er bare opptatt av ting gått, pekeren, ikke tingen pekte på.

Språk som C ++ , C # , PHP er som standard pass-by-verdi som språk ovenfor, men funksjonene kan eksplisitt deklarere parametere for å være pass-by-referanse, ved hjelp av &eller ref.

Perl er alltid bestå-av-referanse; Men i praksis folk nesten alltid kopiere verdiene etter å få det, og dermed bruker den i en pass-by-verdi måte.

Svarte 13/04/2012 kl. 20:00
kilden bruker

stemmer
4

av verdi

  • er langsommere enn ved referanse siden systemet har til å kopiere parameter
  • brukes om inndata bare

ved henvisning

  • raskere, ettersom bare en peker føres
  • benyttes for inngang og utgang
  • kan være meget farlig hvis det brukes sammen med globale variabler
Svarte 05/08/2008 kl. 09:10
kilden bruker

stemmer
3

Når det gjelder J , mens det finnes bare, AFAIK, forbi verdi, er det en form for passering ved referanse som muliggjør å flytte en masse data. Du bare passere noe som kalles en nasjonal innstilling til et verb (eller funksjon). Det kan være en forekomst av en klasse eller bare en generisk beholder.

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

Den åpenbare ulempen er at du trenger å vite navnet på variabelen på noen måte i det som kalles funksjonen. Men denne teknikken kan flytte mye data smertefritt. Det er derfor, mens teknisk sett ikke passerer referanse, jeg kaller det "ganske mye det".

Svarte 21/11/2009 kl. 17:14
kilden bruker

stemmer
2

PHP er også passerer verdi.

<?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";

utganger:

a b

Men i PHP4 gjenstander ble behandlet som primitive . Som betyr:

<?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"
Svarte 11/08/2008 kl. 02:21
kilden bruker

stemmer
-1

Som standard bruker ANSI / ISO C heller - det avhenger av hvordan du erklærer din funksjon og dens parametere.

Hvis du definerer funksjonsparametrene som pekere deretter funksjonen vil være pass-by-referanse, og hvis du deklarerer dine funksjonsparametre som ikke-pekeren variabler deretter funksjonen vil være pass-by-verdi.

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.)

Du kan støte på problemer hvis du oppretter en funksjon som returnerer en peker til en ikke-statisk variabel som ble opprettet i denne funksjonen. Den returnerte verdien av følgende kode ville være udefinert - det er ingen måte å vite om minne allokert til den midlertidige variable opprettet i funksjonen ble overskrevet eller ikke.

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

Man kan imidlertid tilbake en referanse til en statisk variabel eller en peker som ble ført inn i parameterlisten.

float *FtoC(float *temp)
{
    *temp = (*temp-32)*9/5;
    return temp;
}
Svarte 13/01/2011 kl. 20:48
kilden bruker

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