Qual è la differenza tra
- un parametro passato per riferimento
- un parametro passato per valore?
Potresti darmi qualche esempio, per favore?
Qual è la differenza tra
Potresti darmi qualche esempio, per favore?
Risposte:
Innanzitutto, la distinzione "passa per valore vs passa per riferimento", come definita nella teoria CS, è ormai obsoleta perché la tecnica originariamente definita come "passa per riferimento" è caduta da un favore all'altro e raramente viene ora utilizzata. 1
Le lingue più recenti 2 tendono ad usare una coppia diversa (ma simile) di tecniche per ottenere gli stessi effetti (vedi sotto) che è la principale fonte di confusione.
Una fonte secondaria di confusione è il fatto che in "passa per riferimento", "riferimento" ha un significato più ristretto rispetto al termine generale "riferimento" (perché la frase lo precede).
Ora, la definizione autentica è:
Quando un parametro viene passato per riferimento , il chiamante e il chiamante usano la stessa variabile per il parametro. Se la chiamata modifica la variabile del parametro, l'effetto è visibile alla variabile del chiamante.
Quando un parametro viene passato per valore , il chiamante e il chiamante hanno due variabili indipendenti con lo stesso valore. Se la chiamata modifica la variabile del parametro, l'effetto non è visibile al chiamante.
Le cose da notare in questa definizione sono:
"Variabile" qui significa la variabile del chiamante (locale o globale) stessa - cioè se passo una variabile locale per riferimento e la assegno, cambierò la variabile del chiamante stessa, non ad es. Qualunque cosa stia indicando se è un puntatore .
Il significato di "riferimento" in "passa per riferimento" . La differenza con il termine "riferimento" generale è che questo "riferimento" è temporaneo e implicito. Ciò che la chiamata in pratica ottiene è una "variabile" che è in qualche modo "la stessa" di quella originale. Il modo specifico in cui si ottiene questo effetto è irrilevante (ad esempio il linguaggio può anche rivelare alcuni dettagli di implementazione - indirizzi, puntatori, dereferenziazione - tutto ciò è irrilevante; se l'effetto netto è questo, è pass-by-reference).
Ora, nei linguaggi moderni, le variabili tendono ad essere di "tipi di riferimento" (un altro concetto inventato più tardi di "passa per riferimento" e ispirato da esso), vale a dire che i dati dell'oggetto reale vengono memorizzati separatamente da qualche parte (di solito, sull'heap), e solo i "riferimenti" ad esso vengono mai mantenuti in variabili e passati come parametri. 3
Il passaggio di un riferimento di questo tipo rientra nel valore di passaggio perché il valore di una variabile è tecnicamente il riferimento stesso, non l'oggetto indicato. Tuttavia, l'effetto netto sul programma può essere lo stesso del valore per passaggio o del riferimento per passaggio:
Come puoi vedere, questa coppia di tecniche è quasi uguale a quella nella definizione, solo con un livello di riferimento indiretto: basta sostituire "variabile" con "oggetto referenziato".
Non esiste un nome concordato per loro, il che porta a spiegazioni contorte come "chiama per valore dove il valore è un riferimento". Nel 1975, Barbara Liskov ha suggerito il termine " condivisione per oggetto " (o talvolta solo "condivisione per condivisione"), anche se non ha mai preso piede. Inoltre, nessuna di queste frasi traccia un parallelo con la coppia originale. Non sorprende che i vecchi termini finissero per essere riutilizzati in assenza di qualcosa di meglio, portando alla confusione. 4
NOTA : per molto tempo, questa risposta ha usato per dire:
Di 'che voglio condividere una pagina web con te. Se ti dico l'URL, sto passando per riferimento. Puoi usare quell'URL per vedere la stessa pagina web che posso vedere. Se quella pagina viene modificata, entrambi vediamo le modifiche. Se elimini l'URL, tutto ciò che stai facendo è distruggere il tuo riferimento a quella pagina: non stai eliminando la pagina stessa.
Se stampo la pagina e vi do la stampa, sto passando per valore. La tua pagina è una copia disconnessa dell'originale. Non vedrai alcuna modifica successiva e tutte le modifiche apportate (ad es. Scarabocchi sulla stampa) non verranno visualizzate nella pagina originale. Se distruggi la stampa, hai effettivamente distrutto la tua copia dell'oggetto, ma la pagina Web originale rimane intatta.
Questo è per lo più corretto tranne il significato più stretto di "riferimento" - essendo sia temporaneo che implicito (non deve, ma essere esplicito e / o persistente sono caratteristiche aggiuntive, non una parte del semantico pass-by-reference , come spiegato sopra). Un'analogia più stretta ti darebbe una copia di un documento rispetto all'invito a lavorare sull'originale.
1 A meno che non si stia programmando in Fortran o Visual Basic, non è il comportamento predefinito e nella maggior parte dei linguaggi in uso moderno, la vera chiamata per riferimento non è nemmeno possibile.
2 Anche una buona parte di quelli più anziani lo supporta
3 In diverse lingue moderne, tutti i tipi sono tipi di riferimento. Questo approccio è stato introdotto dalla lingua CLU nel 1975 e da allora è stato adottato da molte altre lingue, tra cui Python e Ruby. E molte altre lingue usano un approccio ibrido, in cui alcuni tipi sono "tipi di valore" e altri sono "tipi di riferimento", tra cui C #, Java e JavaScript.
4 Non c'è niente di male nel riciclare un vecchio termine adatto di per sé, ma bisogna in qualche modo chiarire quale significato viene usato ogni volta. Non farlo è esattamente ciò che continua a causare confusione.
È un modo per passare argomenti alle funzioni. Passare per riferimento significa che il parametro delle funzioni chiamate sarà lo stesso dell'argomento passato dei chiamanti (non il valore, ma l'identità - la variabile stessa). Passa per valore significa che il parametro delle funzioni chiamate sarà una copia dell'argomento passato dei chiamanti. Il valore sarà lo stesso, ma l'identità - la variabile - è diversa. Pertanto, la modifica di un parametro fatto dalla funzione chiamata in un caso cambia l'argomento passato e nell'altro caso cambia semplicemente il valore del parametro nella funzione chiamata (che è solo una copia). In fretta:
ref
utilizzata dal chiamante e chiamata funzione). Jon Skeet ha anche una bella spiegazione di questo qui .codici
Dal momento che il mio linguaggio è C ++, lo userò qui
// passes a pointer (called reference in java) to an integer
void call_by_value(int *p) { // :1
p = NULL;
}
// passes an integer
void call_by_value(int p) { // :2
p = 42;
}
// passes an integer by reference
void call_by_reference(int & p) { // :3
p = 42;
}
// this is the java style of passing references. NULL is called "null" there.
void call_by_value_special(int *p) { // :4
*p = 10; // changes what p points to ("what p references" in java)
// only changes the value of the parameter, but *not* of
// the argument passed by the caller. thus it's pass-by-value:
p = NULL;
}
int main() {
int value = 10;
int * pointer = &value;
call_by_value(pointer); // :1
assert(pointer == &value); // pointer was copied
call_by_value(value); // :2
assert(value == 10); // value was copied
call_by_reference(value); // :3
assert(value == 42); // value was passed by reference
call_by_value_special(pointer); // :4
// pointer was copied but what pointer references was changed.
assert(value == 10 && pointer == &value);
}
E un esempio in Java non farà male:
class Example {
int value = 0;
// similar to :4 case in the c++ example
static void accept_reference(Example e) { // :1
e.value++; // will change the referenced object
e = null; // will only change the parameter
}
// similar to the :2 case in the c++ example
static void accept_primitive(int v) { // :2
v++; // will only change the parameter
}
public static void main(String... args) {
int value = 0;
Example ref = new Example(); // reference
// note what we pass is the reference, not the object. we can't
// pass objects. The reference is copied (pass-by-value).
accept_reference(ref); // :1
assert ref != null && ref.value == 1;
// the primitive int variable is copied
accept_primitive(value); // :2
assert value == 0;
}
}
Wikipedia
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference
Questo ragazzo praticamente lo inchioda:
Molte risposte qui (e in particolare la risposta più votata) sono in realtà errate, poiché fraintendono il significato reale di "chiamare per riferimento". Ecco il mio tentativo di chiarire le cose.
In termini più semplici:
In termini metaforici:
Si noti che entrambi questi concetti sono completamente indipendenti e ortogonali al concetto di tipi di riferimento (che in Java sono tutti i tipi che sono sottotipi Object
e in tutti i class
tipi C # ) o al concetto di tipi di puntatori come in C (che sono semanticamente equivalenti ai "tipi di riferimento" di Java, semplicemente con sintassi diversa).
La nozione di tipo di riferimento corrisponde a un URL: è sia essa stessa un'informazione, sia un riferimento (un puntatore , se vuoi) ad altre informazioni. Puoi avere molte copie di un URL in luoghi diversi e non cambiano il sito Web a cui si collegano tutti; se il sito Web viene aggiornato, ogni copia dell'URL porterà comunque alle informazioni aggiornate. Al contrario, la modifica dell'URL in qualsiasi luogo non influirà su qualsiasi altra copia scritta dell'URL.
Si noti che C ++ ha una nozione di "riferimenti" (ad esempio int&
) che non è come i "tipi di riferimento" di Java e C #, ma è come "chiamata per riferimento". I "tipi di riferimento" di Java e C #, e tutti i tipi in Python, sono come quelli che C e C ++ chiamano "tipi di puntatore" (ad es int*
.).
OK, ecco la spiegazione più lunga e più formale.
Per cominciare, desidero evidenziare alcuni aspetti importanti della terminologia, per aiutare a chiarire la mia risposta e per garantire che tutti ci riferiamo alle stesse idee quando usiamo le parole. (In pratica, credo che la stragrande maggioranza della confusione su argomenti come questi derivi dall'uso delle parole in modi che non comunicano pienamente il significato previsto).
Per iniziare, ecco un esempio in un linguaggio di tipo C di una dichiarazione di funzione:
void foo(int param) { // line 1
param += 1;
}
Ed ecco un esempio di chiamata a questa funzione:
void bar() {
int arg = 1; // line 2
foo(arg); // line 3
}
Utilizzando questo esempio, desidero definire alcuni aspetti importanti della terminologia:
foo
è una funzione dichiarata sulla linea 1 (Java insiste nel rendere tutti i metodi delle funzioni, ma il concetto è lo stesso senza perdita di generalità; C e C ++ fanno una distinzione tra dichiarazione e definizione in cui non entrerò qui)param
è un parametro formale per foo
, ha dichiarato anche sulla linea 1arg
è una variabile , in particolare una variabile locale della funzione bar
, dichiarata e inizializzata sulla riga 2arg
è anche un argomento per una specifica invocazione della foo
riga 3Ci sono due serie di concetti molto importanti da distinguere qui. Il primo è valore contro variabile :
bar
funzione sopra, dopo la riga int arg = 1;
, l'espressione arg
ha il valore 1
.final
o C # readonly
) o profondamente immutabile (ad es. Usando C ++ const
).L'altra importante coppia di concetti da distinguere è parametro contro argomento :
In call by value , i parametri formali della funzione sono variabili che sono state appena create per l'invocazione della funzione e che sono inizializzate con i valori dei loro argomenti.
Funziona esattamente nello stesso modo in cui qualsiasi altro tipo di variabile viene inizializzato con valori. Per esempio:
int arg = 1;
int another_variable = arg;
Qui arg
e another_variable
sono variabili completamente indipendenti - i loro valori possono cambiare indipendentemente l'uno dall'altro. Tuttavia, nel punto in cui another_variable
viene dichiarato, viene inizializzato per contenere lo stesso valore che arg
detiene, ovvero 1
.
Poiché sono variabili indipendenti, le modifiche another_variable
non influiscono su arg
:
int arg = 1;
int another_variable = arg;
another_variable = 2;
assert arg == 1; // true
assert another_variable == 2; // true
Questo è esattamente lo stesso della relazione tra arg
e param
nel nostro esempio sopra, che ripeterò qui per simmetria:
void foo(int param) {
param += 1;
}
void bar() {
int arg = 1;
foo(arg);
}
È esattamente come se avessimo scritto il codice in questo modo:
// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here
Cioè, la caratteristica che definisce cosa significa chiamare per valore è che la chiamata ( foo
in questo caso) riceve valori come argomenti, ma ha le sue variabili separate per tali valori dalle variabili del chiamante ( bar
in questo caso).
Tornando alla mia metafora sopra, se io bar
e te foo
, quando ti chiamo, ti passo un pezzo di carta con un valore scritto sopra. Tu chiami quel pezzo di carta param
. Quel valore è una copia del valore che ho scritto sul mio taccuino (le mie variabili locali), in una variabile che chiamo arg
.
(A parte: a seconda dell'hardware e del sistema operativo, ci sono varie convenzioni di chiamata su come chiamate una funzione da un'altra. La convenzione di chiamata è come noi che decidiamo se scrivo il valore su un pezzo del mio foglio e poi ve lo passiamo , o se hai un pezzo di carta su cui lo scrivo, o se lo scrivo sul muro di fronte a entrambi. Anche questo è un argomento interessante, ma ben oltre lo scopo di questa risposta già lunga.)
Nella chiamata per riferimento , i parametri formali della funzione sono semplicemente nuovi nomi per le stesse variabili che il chiamante fornisce come argomenti.
Tornando al nostro esempio sopra, è equivalente a:
// entering function "bar" here
int arg = 1;
// entering function "foo" here
// aha! I note that "param" is just another name for "arg"
arg /* param */ += 1;
// exiting function "foo" here
// exiting function "bar" here
Poiché param
è solo un altro nome per arg
- cioè, sono la stessa variabile , in cui param
si riflettono le modifiche arg
. Questo è il modo fondamentale in cui la chiamata per riferimento differisce dalla chiamata per valore.
Pochissime lingue supportano la chiamata per riferimento, ma C ++ può farlo in questo modo:
void foo(int& param) {
param += 1;
}
void bar() {
int arg = 1;
foo(arg);
}
In questo caso, param
non ha solo lo stesso valore di arg
, in realtà lo è arg
(solo con un nome diverso) e quindi bar
può osservare che arg
è stato incrementato.
Si noti che questo non è il modo in cui oggi funzionano Java, JavaScript, C, Objective-C, Python o quasi qualsiasi altro linguaggio popolare. Ciò significa che quelle lingue non sono chiamate per riferimento, sono chiamate per valore.
Se quello che hai è chiamare per valore , ma il valore effettivo è un tipo di riferimento o un tipo di puntatore , il "valore" in sé non è molto interessante (ad esempio in C è solo un numero intero di una dimensione specifica della piattaforma) - che cos'è interessante è ciò a cui punta quel valore .
Se ciò a cui quel tipo di riferimento (ovvero puntatore) punta è mutabile, allora è possibile un effetto interessante: è possibile modificare il valore puntato e il chiamante può osservare le modifiche al valore puntato, anche se il chiamante non può osservare cambia al puntatore stesso.
Per prendere nuovamente in prestito l'analogia dell'URL, il fatto che ti abbia dato una copia dell'URL a un sito Web non è particolarmente interessante se la cosa che ci interessa sia il sito Web, non l'URL. Il fatto che tu scarabocchi sulla tua copia dell'URL non influisca sulla mia copia dell'URL non è una cosa di cui ci preoccupiamo (e in effetti, in lingue come Java e Python, "URL", o valore del tipo di riferimento, può non sarà affatto modificato, solo la cosa indicata da esso può).
Barbara Liskov, quando inventò il linguaggio di programmazione CLU (che aveva queste semantiche), si rese conto che i termini esistenti "chiamata per valore" e "chiamata per riferimento" non erano particolarmente utili per descrivere la semantica di questo nuovo linguaggio. Quindi ha inventato un nuovo termine: chiamare per condivisione degli oggetti .
Quando parlo di linguaggi tecnicamente chiamati per valore, ma dove tipi comuni in uso sono tipi di riferimento o puntatore (vale a dire: quasi tutti i moderni linguaggi di programmazione imperativi, orientati agli oggetti o multi-paradigma), trovo che sia molto meno confuso evita semplicemente di parlare di chiamata per valore o di chiamata per riferimento . Attenersi alla chiamata per condivisione degli oggetti (o semplicemente chiamare per oggetto ) e nessuno sarà confuso. :-)
The first is value versus variable.
The other important pair of concepts to distinguish is parameter versus argument:
Prima di comprendere i 2 termini, DEVI comprendere quanto segue. Ogni oggetto ha 2 cose che possono farlo distinguere.
Quindi se dici employee.name = "John"
sappi che ci sono 2 cose in merito name
. Il suo valore, che è "John"
anche la sua posizione nella memoria, che è un numero esadecimale forse in questo modo: 0x7fd5d258dd00
.
A seconda dell'architettura del linguaggio o del tipo (classe, struttura, ecc.) Del tuo oggetto, verrai trasferito "John"
o0x7fd5d258dd00
Il passaggio "John"
è noto come passaggio per valore. Il passaggio 0x7fd5d258dd00
è noto come passaggio per riferimento. Chiunque stia puntando a questa posizione di memoria avrà accesso al valore di "John"
.
Per ulteriori informazioni, ti consiglio di leggere sulla dereferenziazione di un puntatore e anche perché scegliere struct (tipo di valore) piuttosto che classe (tipo di riferimento)
Ecco un esempio:
#include <iostream>
void by_val(int arg) { arg += 2; }
void by_ref(int&arg) { arg += 2; }
int main()
{
int x = 0;
by_val(x); std::cout << x << std::endl; // prints 0
by_ref(x); std::cout << x << std::endl; // prints 2
int y = 0;
by_ref(y); std::cout << y << std::endl; // prints 2
by_val(y); std::cout << y << std::endl; // prints 2
}
y
è già stato impostato su 2 dalla riga precedente. Perché dovrebbe tornare a 0?
Il modo più semplice per ottenere questo è su un file Excel. Diciamo ad esempio che hai due numeri, 5 e 2 nelle celle A1 e B1 di conseguenza, e vuoi trovare la loro somma in una terza cella, diciamo A2. Puoi farlo in due modi.
Sia per superamento dei relativi valori di cella A2 digitando = 5 + 2 in questa cella. In questo caso, se i valori delle celle A1 o B1 cambiano, la somma in A2 rimane invariata.
O passando i "riferimenti" delle celle A1 e B1 alla cella A2 digitando = A1 + B1 . In questo caso, se i valori delle celle A1 o B1 cambiano, anche la somma in A2 cambia.
Quando si passa per ref, si passa sostanzialmente a un puntatore alla variabile. Passa per valore che stai passando una copia della variabile. Nell'uso di base questo normalmente significa passare per ref le modifiche alla variabile saranno il metodo chiamante e passeranno per valore che non vogliono.
Passa per valore invia una COPIA dei dati memorizzati nella variabile specificata, passa per riferimento invia un collegamento diretto alla variabile stessa. Pertanto, se si passa una variabile per riferimento e quindi si modifica la variabile all'interno del blocco in cui è stata passata, la variabile originale verrà modificata. Se passi semplicemente per valore, la variabile originale non sarà in grado di essere modificata dal blocco in cui l'hai passata ma otterrai una copia di tutto ciò che conteneva al momento della chiamata.
Passa per valore: la funzione copia la variabile e funziona con una copia (quindi non cambia nulla nella variabile originale)
Passa per riferimento - La funzione utilizza la variabile originale, se si cambia la variabile nell'altra funzione, cambia anche nella variabile originale.
Esempio (copia e usa / prova questo da solo e vedi):
#include <iostream>
using namespace std;
void funct1(int a){ //pass-by-value
a = 6; //now "a" is 6 only in funct1, but not in main or anywhere else
}
void funct2(int &a){ //pass-by-reference
a = 7; //now "a" is 7 both in funct2, main and everywhere else it'll be used
}
int main()
{
int a = 5;
funct1(a);
cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 5
funct2(a);
cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 7
return 0;
}
Mantenerlo semplice, fa capolino. Le pareti del testo possono essere una cattiva abitudine.
Una grande differenza tra loro è che le variabili di tipo valore memorizzano valori, quindi specificando una variabile di tipo valore in una chiamata di metodo si passa una copia del valore di quella variabile al metodo Le variabili del tipo di riferimento memorizzano i riferimenti agli oggetti, pertanto la specifica di una variabile del tipo di riferimento come argomento passa al metodo una copia del riferimento effettivo che fa riferimento all'oggetto. Anche se il riferimento stesso viene passato per valore, il metodo può comunque utilizzare il riferimento che riceve per interagire con - e possibilmente modificare - l'oggetto originale. Allo stesso modo, quando si restituiscono informazioni da un metodo tramite un'istruzione return, il metodo restituisce una copia del valore memorizzato in una variabile di tipo valore o una copia del riferimento memorizzato in una variabile di tipo riferimento. Quando viene restituito un riferimento, il metodo chiamante può utilizzare quel riferimento per interagire con l'oggetto referenziato. Così,
In c #, per passare una variabile per riferimento in modo che il metodo chiamato possa modificare la variabile, C # fornisce le parole chiave ref e out. L'applicazione della parola chiave ref a una dichiarazione di parametro consente di passare una variabile a un metodo per riferimento: il metodo chiamato sarà in grado di modificare la variabile originale nel chiamante. La parola chiave ref viene utilizzata per le variabili che sono già state inizializzate nel metodo chiamante. Normalmente, quando una chiamata al metodo contiene una variabile non inizializzata come argomento, il compilatore genera un errore. Precedendo un parametro con la parola chiave out crea un parametro di output. Ciò indica al compilatore che l'argomento verrà passato nel metodo chiamato per riferimento e che il metodo chiamato assegnerà un valore alla variabile originale nel chiamante. Se il metodo non assegna un valore al parametro di output in ogni possibile percorso di esecuzione, il compilatore genera un errore. Ciò impedisce inoltre al compilatore di generare un messaggio di errore per una variabile non inizializzata che viene passata come argomento a un metodo. Un metodo può restituire un solo valore al suo chiamante tramite un'istruzione return, ma può restituire molti valori specificando più parametri di output (ref e / o out).
vedi discussione c # ed esempi qui link text
Esempi:
class Dog
{
public:
barkAt( const std::string& pOtherDog ); // const reference
barkAt( std::string pOtherDog ); // value
};
const &
è generalmente il migliore. Non incorrere nella pena di costruzione e distruzione. Se il riferimento non è costante, l'interfaccia suggerisce che cambierà i dati passati.
Se non si desidera modificare il valore della variabile originale dopo averla passata in una funzione, la funzione deve essere costruita con un parametro " passa per valore ".
Quindi la funzione avrà SOLO il valore ma non l'indirizzo della variabile passata. Senza l'indirizzo della variabile, il codice all'interno della funzione non può modificare il valore della variabile visto dall'esterno della funzione.
Ma se si desidera dare alla funzione la possibilità di modificare il valore della variabile vista dall'esterno, è necessario utilizzare il passaggio per riferimento . Poiché sia il valore che l'indirizzo (riferimento) vengono passati e disponibili all'interno della funzione.
passare per valore significa come passare il valore a una funzione usando argomenti. in passaggio per valore copiamo i dati memorizzati nella variabile specificata ed è più lento del passaggio per riferimento poiché i dati vengono copiati. di apportiamo modifiche ai dati copiati, i dati originali non sono interessati. e passiamo per riferimento o passiamo per indirizzo inviamo il collegamento diretto alla variabile stessa. o passando il puntatore a una variabile. è più veloce perché si consuma meno tempo
Ecco un esempio che dimostra le differenze tra passa per valore - valore puntatore - riferimento :
void swap_by_value(int a, int b){
int temp;
temp = a;
a = b;
b = temp;
}
void swap_by_pointer(int *a, int *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void swap_by_reference(int &a, int &b){
int temp;
temp = a;
a = b;
b = temp;
}
int main(void){
int arg1 = 1, arg2 = 2;
swap_by_value(arg1, arg2);
cout << arg1 << " " << arg2 << endl; //prints 1 2
swap_by_pointer(&arg1, &arg2);
cout << arg1 << " " << arg2 << endl; //prints 2 1
arg1 = 1; //reset values
arg2 = 2;
swap_by_reference(arg1, arg2);
cout << arg1 << " " << arg2 << endl; //prints 2 1
}
Il metodo "passaggio per riferimento" ha un limite importante . Se un parametro viene dichiarato passato come riferimento (quindi è preceduto dal segno &) il suo parametro effettivo corrispondente deve essere una variabile .
Un parametro reale che si riferisce al parametro formale "passato per valore" può essere un'espressione in generale, quindi è consentito utilizzare non solo una variabile ma anche il risultato di una chiamata letterale o persino di una funzione.
La funzione non è in grado di inserire un valore in qualcosa di diverso da una variabile. Non può assegnare un nuovo valore a un valore letterale o forzare un'espressione a modificarne il risultato.
PS: Puoi anche controllare la risposta di Dylan Beattie nel thread corrente che lo spiega in parole semplici.