47

При изучении нового языка программирования одним из возможных препятствий, с которыми вы можете столкнуться, является вопрос о том, является ли язык по умолчанию пропуском или передачей по ссылке.Пройти по ссылке?

Итак, вот мой вопрос для всех вас, на вашем любимом языке, как действительно ли это сделано? И каковы возможных подводных камней?

Ваш любимый язык может, конечно, быть все, что вы когда-либо играли: popular, obscure, esoteric, new, old ...

+0

Здесь есть [уже ответ] (http://stackoverflow.com/questions/879/php-variables-passed-by-value-or-by-reference), который объясняет ситуацию в PHP5. – Mat 2008-08-05 09:02:31

+4

Wow - этот вопрос имеет ссылку на бета-сервер. Я думаю, вы бета-исправление ссылки. – 2010-06-10 04:06:19

ответ

31

Вот мой собственный вклад в Java programming language.

первый некоторый код:

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

вызова этого метода приведет в этом:

int pi = 3; 
int everything = 42; 

swap(pi, everything); 

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

"Output: 
pi: 3 
everything: 42" 

даже с помощью 'реальных' объектов будет показывать подобный результат:

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" 

таким образом, ясно, что Java передает свои параметры по значению, так как значение для pi и все и Объекты MyObj не заменены. помните, что «по значению» является только способом в java для передачи параметров методу. (Например, язык, как C++ позволяет разработчику передать параметр по ссылке с помощью «&» после типа параметра)

теперь сложная часть или, по крайней мере, та часть, которая будет путать большинство новых разработчики Java: (заимствованные из javaworld)
Первоначальный автор: Tony Синтес

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" 

сложно успешно изменяет значение pnt1! Это означает, что объекты передаются по ссылке, это не так! Правильный оператор: Ссылки на объекты передаются по значению.

больше от Тони Синтес:

Метод успешно изменяет значение pnt1 , несмотря на то, что передается по значению; однако, своп pnt1 и pnt2 терпит неудачу! Это основной источник путаницы. В основном() методе, pnt1 и pnt2 больше не являются , чем ссылки на объекты. Когда вы передаете pnt1 и pnt2 в метод tricky(), Java передает ссылки по значению , как и любой другой параметр.Это означает, что ссылки, переданные методу , фактически являются копиями исходных ссылок . На рисунке 1 ниже показаны две ссылки, указывающие на объект , после того как Java передает объект в метод.

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

Заключение или длинный рассказ короткий:

  • Java передает его параметры по значению
  • "по значению" является единственным способом в java передать параметр методу
  • с использованием методов от объекта, заданного как параметр , изменит объект как точку ссылки на исходные объекты. (Если сам метод изменяет некоторые значения)

полезные ссылки:

4

значением

  • медленнее, чем по ссылке, так как система должна скопировать параметр
  • используется только для входа

по ссылке

  • быстрее, так как передается только указатель
  • используется для ввода и выход
  • может быть очень опасно, если они используются в сочетании с глобальными переменными
+0

На самом деле не отвечает на вопрос, но +1 для определения фактов. – MPelletier 2009-11-21 16:49:49

5

Не забывайте, что есть также проход по имени и проход по ценностной результате.

Передача по значению-результату аналогична передаче по значению, с добавленным аспектом, что значение задано в исходной переменной, которая была передана в качестве параметра. Это может в какой-то мере избежать вмешательства в глобальные переменные. По-видимому, это лучше в секционированной памяти, где пропуск по ссылке может вызвать ошибку страницы (Reference).

Пропустить по имени означает, что значения вычисляются только тогда, когда они фактически используются, а не в начале процедуры.Алгол использовал pass-by-name, но интересным побочным эффектом является то, что очень сложно написать процедуру свопинга (Reference). Кроме того, выражение, переданное по имени, переучитывается каждый раз, когда к нему обращаются, что также может иметь побочные эффекты. не

20

Вот еще одна статья для c# programming language

C# передает свои аргументы по значению (по умолчанию)

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

называя эту версию свопа таким образом будет иметь никакого результата:

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

"output: 
x: foo 
y: bar" 

однако unlike java C# дает разработчику возможность передать параметры по ссылке, это делается с помощью «реф» ключевое слово перед типом параметра:

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

этот своп будет изменить значение ссылочного параметра :

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

"output: 
x: bar 
y: foo" 

C# также имеет из ключевого слова, а разница между реф и из тонка. from msdn:

, вызывающий метод, который принимает из параметра не требуется, чтобы присвоить переменной передается в качестве параметра из до вызова; однако, вызываемый требуется для назначить параметр out перед возвращение.

и

В отличие параметры реф являются считается изначально назначен по вызываемым. Таким образом, вызываемым пользователем является , а не , которое необходимо присвоить параметру перед использованием. Параметры параметров передаются как в метод , так и вне его.

небольшая ловушки есть, как и в Java, что объектов, передаваемых по значению все еще могут быть изменены с помощью своих внутренних методов

вывода:

  • C# передает свои параметры, с помощью по умолчанию, по значению
  • , но при необходимости параметры также могут быть переданы со ссылкой, используя реф ключевое слово
  • внутренние методы из параметра, передаваемого по значению будет изменять объект (если сам метод изменяет некоторые значения)

полезные ссылки:

6

Для .NET существует good explanation here.

Многие люди удивляются тому, что ссылочные объекты фактически передаются по значению (как на C#, так и на Java). Это копия адреса стека. Это предотвращает изменение метода, на который указывает объект, но все же позволяет методу изменять значения объекта. В C# его можно передать ссылку по ссылке, что означает, что вы можете изменить место, на которое указывает фактический объект.

2

PHP также передается по значению.

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

Выходы:

a b 

Однако в объекты PHP4 относились как primitives. Что означает:

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

Python использует передачу по значению, но так как все эти значения являются ссылками на объектах, чистый эффект есть нечто родственное пройти по ссылке. Однако программисты Python больше думают о том, является ли тип объекта изменчивым или неизменным. Измененные объекты могут быть изменены на месте (например, словари, списки, определяемые пользователем объекты), тогда как неизменяемые объекты не могут (например, целые числа, строки, кортежи).

В следующем примере показана функция, передающая два аргумента, неизменяемая строка и изменяемый список.

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

Линия a = "Red" просто создает локальное имя a, для строкового значения "Red" и не оказывает никакого влияния на переданное в аргументе (который теперь скрыт, как и a должны ссылаться на локальное имя с тех пор). Назначение - это не операция на месте, независимо от того, является ли аргумент изменчивым или неизменяемым.

Параметр b является ссылкой на изменяемый объект списка, а метод .append() выполняет расширение на месте списка, привязывая к новому строковому значению "Blue".

(Поскольку строковые объекты являются неизменными, они не имеют каких-либо методов, которые поддерживают в месте изменения.)

После того, как функция возвращает, повторное назначение a не имели никакого эффекта, в то время как расширение b ясно показывает семантику стиля вызова по ссылке.

Как упоминался ранее, даже если аргумент a является изменяемым типом, повторное назначение в функции не операция на месте, и поэтому не будет никаких изменений в значение пройденного аргумента:

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

Если вы не хотите, чтобы ваш список изменялся вызываемой функцией, вместо этого вы использовали бы неизменяемый тип кортежа (обозначенный скобками в литеральной форме, а не квадратными скобками), что не поддерживает на месте .append() метод:

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

Из того, что я прочитал в быстрой проверке аргументов Python, проходящей дискуссии в Интернете, большинство людей Python не знают, что означает пересылка по ссылке. Python определенно проходит по значению. Неизменность ценностей - отдельная проблема. И тогда есть люди, которые путаются со связями со словарем и не понимают, что привязка символа к ссылке на значение в словаре - это то же самое, что и переменная, содержащая ссылку на значение. Передайте по ссылке, где вы передаете ссылку на переменную, а не на значение; или в символьном языке, где вы передаете изменяемое имя. – 2010-02-13 06:02:37

3

Относительно J, в то время как есть только, AFAIK, проходящий по значению, есть форма прохождения по ссылке, которая позволяет перемещать много данных. Вы просто передаете слово, известное как локаль, в глагол (или функцию). Это может быть экземпляр класса или просто общий контейнер.

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 

Очевидным недостатком является то, что вам нужно знать имя вашей переменной каким-то образом в вызываемой функции. Но этот метод может безболезненно перемещать множество данных. Вот почему, пока технически не проходят по ссылке, я называю это «в значительной степени».

-1

По умолчанию ANSI/ISO C использует либо - это зависит от того, как вы объявляете свою функцию и ее параметры.

Если вы объявляете свои параметры функции в качестве указателей, тогда функция будет передаваться по ссылке, и если вы объявите свои параметры функции в качестве переменных не указателя, тогда функция будет передаваться по значению.

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

Вы можете столкнуться с проблемами, если вы создаете функцию, которая возвращает указатель на не-статической переменной, которая была создана в рамках этой функции. Возвращенное значение следующего кода будет неопределенным - нет способа узнать, было ли пространство памяти, выделенное временной переменной, созданной в этой функции, было перезаписано или нет.

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

Вы можете, однако, вернуть ссылку на статическую переменную или указатель, который был передан в списке параметров.

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

-1. Это не верно. C всегда передается по значению. Если вы объявляете параметр `int`,` int` будет передаваться по значению, если объявить параметр `` float``, `float` будет передаваться по значению, и если вы объявите параметр чтобы быть указателем, указатель будет передан по значению, но есть * никогда * pass-by-reference. – 2012-02-25 04:11:07

7

Поскольку я еще не видел ответа Perl, я думал, что напишу его.

Под капотом Perl эффективно работает как пропуск по ссылке. Переменные как аргументы вызова функции передаются по ссылке, константы передаются как значения только для чтения, а результаты выражений передаются как временные.Обычные идиомы для построения списка аргументов по заданию списка из @_, или shift, как правило, чтобы скрыть это от пользователя, давая появление пройти по значению:

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

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

Это будет печатать Value is now 1 потому что $x++ увеличивших лексическая переменная, объявленная в функции incr(), а не переменная, переданная в. Этот стиль пошагового значения обычно используется в большинстве случаев, поскольку функции, которые изменяют свои аргументы, редко встречаются в Perl, и стиль следует избегать ,

Однако, если по какой-либо причине это поведение является особенно желательным, его можно достичь, действуя непосредственно на элементы массива @_, поскольку они будут алиасами для переменных, передаваемых в функцию.

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

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

На этот раз он будет печатать Value is now 2, поскольку выражение $_[0]++ увеличивается фактическое $value переменной. То, как это работает, заключается в том, что под капотом @_ не является реальным массивом, как большинство других массивов (например, будет получено my @array), но вместо этого его элементы создаются непосредственно из аргументов, переданных вызову функции. Это позволяет вам построить семантику pass-by-reference, если это потребуется. Аргументы вызова функции, которые являются равными переменными, вставляются как-есть в этот массив, а константы или результаты более сложных выражений вставляются как временные данные только для чтения.

Однако на практике это редко случается, поскольку Perl поддерживает опорные значения; то есть значения, которые относятся к другим переменным. Обычно гораздо яснее построить функцию, которая имеет очевидный побочный эффект для переменной, передавая ссылку на эту переменную. Это четкое указание читателю на call-сайте, что семантика передачи по ссылке действует.

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

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

Здесь оператор \ дает ссылку на так же, как & адреса оператора в С.

4

Что бы вы сказать, как передать по значению или пройти по ссылке должен быть согласованный между языками. Наиболее распространенным и последовательным определением, используемым во всех языках, является то, что с помощью pass-by-reference вы можете передать переменную функции «обычно» (т.е. без явно адреса или чего-то подобного), и функция может назначить (не мутировать содержимое) параметра внутри функции, и он будет иметь тот же эффект, что и назначение переменной в области вызова.

С этой точки зрения языки сгруппированы следующим образом; причем каждая группа имеет одну и ту же проходящую семантику. Если вы считаете, что два языка не должны помещаться в одну группу, я призываю вас придумать пример, который их отличает.

Подавляющее большинство языков, включая C , Java, Python, Рубин, JavaScript, Схема, OCaml, Standard ML, Go, объективно- C, Smalltalk и т. Д. Все только переходящий.Передача значения указателя (некоторые языки называют его «ссылкой») не считается передачей по ссылке; нас беспокоит только переданная вещь, указатель, а не то, на что указывает.

Языки, такие как C++, C#, PHP, по умолчанию проходят по значению, как вышеуказанных языков, но функции могут явно объявить параметры будут проходить по ссылке, используя & или ref.

Perl всегда передается по ссылке; однако на практике люди почти всегда копируют значения после их получения, таким образом, используя его по-разному.

Смежные вопросы