Domanda

Quando impari un nuovo linguaggio di programmazione, uno dei possibili ostacoli che potresti incontrare è la domanda se il linguaggio è, per impostazione predefinita, passaggio per valore o passaggio per riferimento.

Quindi ecco la mia domanda a tutti voi, nella vostra lingua preferita: Come è effettivamente fatto?E quali sono i possibili insidie?

La tua lingua preferita può, ovviamente, essere qualsiasi cosa con cui hai giocato: popolare, oscuro, esoterico, nuovo, vecchio...

Nessuna soluzione corretta

Altri suggerimenti

Ecco il mio contributo per il Linguaggio di programmazione Java.

prima un po' di codice:

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

chiamando questo metodo si otterrà quanto segue:

int pi = 3;
int everything = 42;

swap(pi, everything);

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

"Output:
pi: 3
everything: 42"

anche usando oggetti "reali" mostrerà un risultato simile:

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"

quindi è chiaro che Java passa i suoi parametri per valore, come valore per pi E qualunque cosa e il Oggetti MyObj non vengono scambiati.tieni presente che "per valore" è il unico modo in Java per passare i parametri a un metodo.(ad esempio un linguaggio come C++ consente allo sviluppatore di passare un parametro per riferimento utilizzando '&' dopo il tipo del parametro)

ora il parte difficile, o almeno la parte che confonderà la maggior parte dei nuovi sviluppatori Java:(preso in prestito da javaworld)
Autore originale: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"

scaltro modifica con successo il valore di pnt1!Ciò implicherebbe che gli oggetti vengano passati per riferimento, non è questo il caso!Una affermazione corretta sarebbe: IL Riferimenti agli oggetti vengono passati per valore.

altro da Tony Sintes:

Il metodo altera correttamente il valore di PNT1, anche se viene passato per valore;Tuttavia, uno scambio di PNT1 e PNT2 fallisce!Questa è la principale fonte di confusione.Nel metodo principale (), PNT1 e PNT2 non sono altro che riferimenti a oggetti.Quando passi PNT1 e PNT2 al metodo Tricky (), Java passa i riferimenti per valore proprio come qualsiasi altro parametro.Ciò significa che i riferimenti passati al metodo sono in realtà copie dei riferimenti originali.La Figura 1 di seguito mostra due riferimenti che indicano lo stesso oggetto dopo che Java passa un oggetto a un metodo.

figure 1
(fonte: javaworld.com)

Conclusione o per farla breve:

  • Java gli passa i parametri per valore
  • "per valore" è il unico modo in Java per passare un parametro a un metodo
  • utilizzando metodi dall'oggetto dato come parametro altererà l'oggetto poiché i riferimenti puntano agli oggetti originali.(se quel metodo stesso altera alcuni valori)

link utili:

Ecco un altro articolo per il linguaggio di programmazione c#

c# passa i suoi argomenti per valore (per impostazione predefinita)

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

chiamare questa versione di swap non avrà quindi alcun risultato:

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

"output: 
x: foo
y: bar"

Tuttavia, a differenza di Java C# fa dare allo sviluppatore l'opportunità di passare parametri come riferimento, questo viene fatto utilizzando la parola chiave 'ref' prima del tipo del parametro:

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

questo scambio Volere modificare il valore del parametro di riferimento:

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

"output: 
x: bar
y: foo"

c# ha anche un la parola chiave, e la differenza tra ref e out è sottile.da msdn:

Il chiamante di un metodo che accetta un file parametro out non è tenuto a assegnare alla variabile passata come parametro OUT prima della chiamata;tuttavia, il chiamato lo è richiesto di assegnare al parametro OUT prima di restituire.

E

In contrasto parametri di riferimento Sono considerato inizialmente assegnato dal Callee.In quanto tale, il chiamato lo è non richiesto di assegnare al refparametro prima dell'uso.I parametri REF vengono passati sia dentro che fuori da un metodo.

una piccola trappola è, come in Java, quella gli oggetti passati per valore possono ancora essere modificati utilizzando i loro metodi interni

conclusione:

  • c# passa i suoi parametri, per impostazione predefinita, per valore
  • ma quando necessario è possibile passare anche i parametri come riferimento utilizzando la parola chiave ref
  • metodi interni da un parametro passato per valore altererà l'oggetto (se quel metodo stesso altera alcuni valori)

link utili:

Pitone utilizza pass-by-value, ma poiché tutti questi valori sono riferimenti a oggetti, l'effetto netto è qualcosa di simile al pass-by-reference.Tuttavia, i programmatori Python pensano di più se un tipo di oggetto lo sia mutevole O immutabile.Gli oggetti mutabili possono essere modificati sul posto (ad esempio, dizionari, elenchi, oggetti definiti dall'utente), mentre gli oggetti immutabili non possono (ad esempio, numeri interi, stringhe, tuple).

L'esempio seguente mostra una funzione a cui vengono passati due argomenti, una stringa immutabile e un elenco modificabile.

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

La linea a = "Red" crea semplicemente un nome locale, a, per il valore della stringa "Red" e non ha alcun effetto sull'argomento passato (che ora è nascosto, come a da quel momento in poi dovrà fare riferimento al nome locale).L'assegnazione non è un'operazione sul posto, indipendentemente dal fatto che l'argomento sia modificabile o immutabile.

IL b il parametro è un riferimento a un oggetto elenco mutabile e il .append() esegue un'estensione sul posto dell'elenco, aggiungendo il nuovo "Blue" valore stringa.

(Poiché gli oggetti stringa sono immutabili, non dispongono di metodi che supportino le modifiche sul posto.)

Una volta restituita la funzione, la riassegnazione di a non ha avuto alcun effetto, mentre l'estensione del b mostra chiaramente la semantica delle chiamate in stile pass-by-reference.

Come accennato prima, anche se l'argomento a favore a è un tipo mutabile, la riassegnazione all'interno della funzione non è un'operazione sul posto e quindi non ci sarebbe alcuna modifica al valore dell'argomento passato:

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

Se non desideri che la tua lista venga modificata dalla funzione chiamata, utilizzeresti invece il tipo di tupla immutabile (identificato dalle parentesi nella forma letterale, anziché dalle parentesi quadre), che non supporta il metodo sul posto .append() metodo:

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

Dato che non ho ancora visto una risposta Perl, ho pensato di scriverne una.

Dietro le quinte, Perl funziona efficacemente come pass-by-reference.Le variabili come argomenti di chiamata di funzione vengono passate in modo referenziale, le costanti vengono passate come valori di sola lettura e i risultati delle espressioni vengono passati come temporanei.I soliti modi di dire da cui costruire liste di argomenti tramite assegnazione di lista @_, o da shift tendono a nasconderlo all'utente, dando l'impressione di un valore pass-by:

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

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

Questo verrà stampato Value is now 1 perché il $x++ ha incrementato la variabile lessicale dichiarata all'interno del file incr() funzione, anziché la variabile passata.Questo stile di passaggio per valore è solitamente ciò che si desidera la maggior parte delle volte, poiché le funzioni che modificano i propri argomenti sono rare in Perl e lo stile dovrebbe essere evitato.

Se però per qualche motivo si desidera espressamente questo comportamento, è possibile ottenerlo agendo direttamente sugli elementi del file @_ array, perché saranno alias per le variabili passate nella funzione.

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

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

Questa volta verrà stampato Value is now 2, perché il $_[0]++ l'espressione ha incrementato l'effettivo $value variabile.Il modo in cui funziona è quello sotto il cofano @_ non è un vero array come la maggior parte degli altri array (come sarebbe ottenuto da my @array), ma i suoi elementi sono invece costruiti direttamente dagli argomenti passati a una chiamata di funzione.Ciò consente di costruire una semantica pass-by-reference se ciò fosse necessario.Gli argomenti delle chiamate di funzione che sono variabili semplici vengono inseriti così come sono in questo array e le costanti o i risultati di espressioni più complesse vengono inseriti come temporanei di sola lettura.

Tuttavia è estremamente raro che ciò avvenga nella pratica, perché Perl supporta i valori di riferimento;cioè valori che si riferiscono ad altre variabili.Normalmente è molto più chiaro costruire una funzione che abbia un evidente effetto collaterale su una variabile, passando un riferimento a quella variabile.Questa è una chiara indicazione per il lettore sul sito di chiamata che la semantica pass-by-reference è in vigore.

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

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

Ecco il \ l'operatore produce un riferimento più o meno allo stesso modo dell'operatore & indirizzo dell'operatore in C.

C'è un buona spiegazione qui per .NET.

Molte persone sono sorprese dal fatto che gli oggetti di riferimento vengano effettivamente passati per valore (sia in C# che in Java).È una copia di un indirizzo di stack.Ciò impedisce a un metodo di modificare il punto a cui punta effettivamente l'oggetto, ma consente comunque a un metodo di modificare i valori dell'oggetto.In C# è possibile passare un riferimento per riferimento, il che significa che puoi modificare il punto a cui punta un oggetto reale.

Non dimenticare che c'è anche passare per nome, E passare per valore-risultato.

Il passaggio per valore-risultato è simile al passaggio per valore, con l'aggiunta che il valore è impostato nella variabile originale passata come parametro.Può, in una certa misura, evitare interferenze con le variabili globali.Apparentemente è migliore nella memoria partizionata, dove un passaggio per riferimento potrebbe causare un errore di pagina (Riferimento).

Passa per nome significa che i valori vengono calcolati solo al momento dell'effettivo utilizzo e non all'inizio della procedura.Algol utilizzava il pass-by-name, ma un effetto collaterale interessante è che è molto difficile scrivere una procedura di swap (Riferimento).Inoltre, l'espressione passata per nome viene rivalutata ogni volta che si accede, il che può avere anche effetti collaterali.

per valore

  • è più lento rispetto al riferimento poiché il sistema deve copiare il parametro
  • utilizzato solo per l'input

come riferimento

  • più velocemente poiché viene passato solo un puntatore
  • utilizzato per l'input E produzione
  • può essere molto pericoloso se usato insieme a variabili globali

Qualunque cosa tu dica come valore pass-by o riferimento pass-by deve essere coerente tra le lingue.La definizione più comune e coerente utilizzata in tutti i linguaggi è che con il pass-by-reference è possibile passare una variabile a una funzione "normalmente" (cioèsenza prendere esplicitamente l'indirizzo o qualcosa del genere), e la funzione può assegnato a (non modificare il contenuto del) parametro all'interno della funzione e avrà lo stesso effetto dell'assegnazione alla variabile nell'ambito chiamante.

Da questo punto di vista, le lingue sono raggruppate come segue;ciascun gruppo ha la stessa semantica di passaggio.Se pensi che due lingue non debbano essere messe nello stesso gruppo, ti sfido a fare un esempio che le distingua.

La stragrande maggioranza delle lingue incluso C, Giava, Pitone, Rubino, JavaScript, schema, OCaml, ML standard, Andare, Obiettivo-C, Chiacchiere, eccetera.sono tutti solo valore pass-by.Il passaggio di un valore del puntatore (alcuni linguaggi lo chiamano "riferimento") non conta come passaggio per riferimento;ci interessa solo la cosa passata, l'indicatore, non la cosa puntata.

Lingue come C++, C#, PHP sono di default pass-by-value come i linguaggi sopra, ma le funzioni possono dichiarare esplicitamente i parametri come pass-by-reference, usando & O ref.

Perl è sempre pass-by-reference;tuttavia, in pratica le persone quasi sempre copiano i valori dopo averli ottenuti, utilizzandoli quindi in modalità pass-by-value.

Riguardo J, mentre esiste solo, per quanto ne so, il passaggio per valore, esiste una forma di passaggio per riferimento che consente di spostare molti dati.Devi semplicemente passare qualcosa noto come locale a un verbo (o funzione).Può essere un'istanza di una classe o semplicemente un contenitore generico.

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

L'ovvio svantaggio è che è necessario conoscere in qualche modo il nome della variabile nella funzione chiamata.Ma questa tecnica può spostare molti dati senza problemi.Ecco perché, anche se tecnicamente non passo per riferimento, lo chiamo "praticamente così".

Anche PHP viene passato per valore.

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

Uscite:

a b

Tuttavia in PHP4 gli oggetti venivano trattati come primitivi.Che significa:

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

Per impostazione predefinita, ANSI/ISO C utilizza entrambi: dipende da come dichiari la funzione e i suoi parametri.

Se dichiari i parametri della tua funzione come puntatori, la funzione sarà pass-by-reference e se dichiari i parametri della tua funzione come variabili non puntatori, la funzione sarà pass-by-value.

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

Potresti riscontrare problemi se crei una funzione che restituisce un puntatore a una variabile non statica creata all'interno di quella funzione.Il valore restituito del codice seguente sarebbe indefinito: non c'è modo di sapere se lo spazio di memoria allocato alla variabile temporanea creata nella funzione è stato sovrascritto o meno.

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

Potresti, tuttavia, restituire un riferimento a una variabile statica o un puntatore passato nell'elenco dei parametri.

float *FtoC(float *temp)
{
    *temp = (*temp-32)*9/5;
    return temp;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top