При изучении нового языка программирования одним из возможных препятствий, с которыми вы можете столкнуться, является вопрос, является ли язык по умолчанию передача по значению или по ссылке.
Итак, вот мой вопрос всем вам, на вашем любимом языке, как, действительно ли это сделано? А что такое возможные подводные камни?
Ваш любимый язык, конечно, может быть любым, с чем вы когда-либо играли: популярный, затемнять, эзотерический, новый, Старый ...
Вау - в этом вопросе есть ссылка на бета-сервер. Думаю, вы исправили бы ссылку бета-версией.





Вот мой собственный вклад в Язык программирования Java.
сначала код:
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 передает свои параметры по стоимости, поскольку значения число Пи и все и MyObj объекты не меняются местами. имейте в виду, что «по значению» - это единственный способ в java для передачи параметров методу. (например, такой язык, как C++, позволяет разработчику передавать параметр по ссылке, используя '&' после типа параметра)
теперь сложная часть, или, по крайней мере, часть, которая запутает большинство новых разработчиков Java: (заимствовано из javaworld)
Оригинальный автор: Тони Синтес
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! Это означало бы, что объекты передаются по ссылке, это не так! Правильным заявлением будет: Ссылки на объекты передаются по значению.
другие работы Тони Синтеса:
The method successfully alters the value of pnt1, even though it is passed by value; however, a swap of pnt1 and pnt2 fails! This is the major source of confusion. In the main() method, pnt1 and pnt2 are nothing more than object references. When you pass pnt1 and pnt2 to the tricky() method, Java passes the references by value just like any other parameter. This means the references passed to the method are actually copies of the original references. Figure 1 below shows two references pointing to the same object after Java passes an object to a method.

(source: javaworld.com)
Вывод или короче:
Полезные ссылки:
по стоимости
по ссылке
На самом деле не отвечает на вопрос, но +1 за установление фактов.
Не забывайте, что есть также пройти по имени и пройти по значению-результату.
Передача по значению-результату аналогична передаче по значению с добавленным аспектом, заключающимся в том, что значение устанавливается в исходной переменной, которая была передана в качестве параметра. Это может до некоторой степени избежать вмешательства в глобальные переменные. Очевидно, это лучше для многораздельной памяти, где передача по ссылке может вызвать сбой страницы (Ссылка).
Передача по имени означает, что значения вычисляются только тогда, когда они фактически используются, а не в начале процедуры. Алгол использовал передачу по имени, но интересным побочным эффектом является то, что очень сложно написать процедуру подкачки (Ссылка). Кроме того, выражение, переданное по имени, переоценивается каждый раз при обращении к нему, что также может иметь побочные эффекты.
Вот еще одна статья для язык программирования C#
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"
однако в отличие от Java C# делает дает разработчику возможность передавать параметры по ссылке, это делается с помощью ключевого слова ref перед типом параметра:
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# также имеет ключевое слово out, и разница между ref и out тонкая. из msdn:
The caller of a method which takes an out parameter is not required to assign to the variable passed as the out parameter prior to the call; however, the callee is required to assign to the out parameter before returning.
и
In contrast ref parameters are considered initially assigned by the callee. As such, the callee is not required to assign to the ref parameter before use. Ref parameters are passed both into and out of a method.
небольшая ошибка, как и в java, в том, что объекты, переданные по значению, по-прежнему могут быть изменены с помощью их внутренних методов
вывод:
Полезные ссылки:
Для .NET есть хорошее объяснение здесь.
Многие люди удивляются, что ссылочные объекты на самом деле передаются по значению (как в C#, так и в Java). Это копия адреса стека. Это предотвращает изменение метода, на которое фактически указывает объект, но по-прежнему позволяет методу изменять значения объекта. В C# можно передать ссылку по ссылке, что означает, что вы можете изменить место, на которое указывает фактический объект.
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 объекты обрабатывались как примитивы. Что значит:
<?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"
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'
Из того, что я прочитал в кратком обзоре аргументов Python, обсуждаемых в сети, большинство людей, занимающихся Python, не знают, что означает передача по ссылке. Python определенно передается по значению. Неизменность ценностей - отдельный вопрос. Кроме того, есть люди, которые путаются со словарными привязками и не понимают, что привязка символа к ссылке на значение в словаре - это то же самое, что и переменная, содержащая ссылку на значение. При передаче по ссылке вы передаете ссылку на переменную, а не на значение; или на языке символов, где вы передаете изменяемую привязку имени.
Что касается 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
Очевидным недостатком является то, что вам нужно каким-то образом знать имя вашей переменной в вызываемой функции. Но этот метод может безболезненно перемещать большой объем данных. Вот почему, хотя технически это не передается по ссылке, я называю это «почти так».
По умолчанию 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;
}
-1. Это не правильно. C всегда передается по значению. Если вы объявляете параметр как int, int будет передан по значению, если вы объявите параметр как float, float будет передан по значению, а если вы объявите параметр как указатель, указатель будет передаваться по значению, но есть передача никогда по ссылке.
@ JörgWMittag: В ANSI / ISO C указатели являются ссылками.
Совершенно неправильно. Параметры в обеих функциях обмена являются значениями. Разница только в типе. Первая сигнатура определяет два параметра указателя на int, вторая определяет параметры типа int.
Поскольку я еще не видел ответа 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), а вместо этого его элементы строятся непосредственно из аргументов, переданных при вызове функции. Это позволяет вам создавать семантику передачи по ссылке, если это необходимо. Аргументы вызова функции, которые являются простыми переменными, вставляются в этот массив как есть, а константы или результаты более сложных выражений вставляются как временные объекты только для чтения.
Однако на практике это делается очень редко, потому что Perl поддерживает ссылочные значения; то есть значения, относящиеся к другим переменным. Обычно гораздо проще создать функцию, которая имеет очевидный побочный эффект для переменной, передав ссылку на эту переменную. Это явное указание читателю на сайте вызова о том, что действует семантика передачи по ссылке.
sub incr_ref {
my ( $ref ) = @_;
$$ref++;
}
my $value = 1;
incr($value);
say "Value is now $value";
Здесь оператор \ дает ссылку во многом так же, как оператор адреса & в C.
Все, что вы говорите как передача по значению или по ссылке, должно быть согласовано для разных языков. Наиболее распространенное и согласованное определение, используемое в разных языках, заключается в том, что с передачей по ссылке вы можете передать переменную функции «нормально» (т.е. без явного указания адреса или чего-либо подобного), а функция может назначить на (не изменять содержимое) параметра внутри функции, и это будет иметь тот же эффект, что и присвоение переменной в вызывающей области.
С этой точки зрения языки сгруппированы следующим образом; каждая группа имеет одинаковую семантику передачи. Если вы считаете, что два языка не следует помещать в одну группу, я призываю вас привести пример, который их различает.
Подавляющее большинство языков, включая C, Ява, Python, Рубин, JavaScript, Схема, OCaml, Стандартный ML, Идти, Цель-C, Болтовня_. Передача значения указателя (некоторые языки называют его «ссылкой») не считается передачей по ссылке; нас беспокоит только переданная вещь, указатель, а не то, на что указывает.
Такие языки, как C++, C#, PHP, по умолчанию передаются по значению, как и языки выше, но функции могут явно объявлять параметры как передаваемые по ссылке, используя & или ref.
Perl всегда передается по ссылке; однако на практике люди почти всегда копируют значения после их получения, таким образом используя их в качестве передачи по значению.
C не должен быть в той же группе, что и Java и подобные, потому что в C можно взять адрес переменной и передать его функции. Это позволяет вызываемой функции изменять значение переменной. То есть в C. можно выполнять передачу по ссылке.
@fishinear: Нет. Это передача по значению. Он копирует переданное значение (указатель).
Точно. В C можно реализовать концептуальную передачу по ссылке, передав указатель на переменную. Это невозможно на таком языке, как Java, и поэтому C (и Objective-C) должны быть в отдельной категории.
@fishinear: Нет. Передача по значению и передача по ссылке - это концепции семантики, связанные со структурой синтаксиса. Это не имеет ничего общего с «концептуальным». В C или Objective-C нет передачи по ссылке.
Вы спросили, почему два языка не должны быть в одной категории. Я даю вам такую вескую причину (можно добиться концептуальной передачи по ссылке в C, но не в Java), и вы отвечаете, сунув голову в песок. Не очень идет.
@fishinear: Ваше «концептуальное» определение не совсем точно. Фактически, можно выполнить «концептуальную передачу по ссылке» на любом языке. В Java: просто. Просто используйте массив из 1 элемента вместо всех переменных. Чтобы прочитать переменную, обратитесь к элементу 0. Чтобы записать в переменную, напишите в элемент 0. А когда вы «переходите по ссылке», просто передайте массив.
Я использовал передачу по ссылке в обычном определении: возможность изменять значение переменной, которая передается как параметр. То, что вы описываете, не меняет значения самой переменной: она по-прежнему указывает на тот же массив. Вы просто меняете содержимое массива. В C можно изменить значение самой переменной.
@fishinear: Нет, это не так. Вы также не изменяете значение того, что передается в вашей вещи в C. Вам нужно сначала взять адрес переменной, которую вы изменяете, с помощью оператора & в C, а затем передать полученный указатель. Этот указатель (если это была переменная в исходной области видимости) не изменяется. Таким образом, то, что вы описываете, в равной степени не является «изменением значения переменной, переданной в качестве параметра».
Вы, кажется, не понимаете, что я полностью согласен с тем, что вы говорите. Но кажется, что вы все еще неправильно понимаете то, что я говорю, или сознательно ограничиваетесь. В C вы можете изменить значение переменной в вызывающей функции из вызываемой функции, передав ее в качестве параметра. В этом суть передачи по ссылке, и программисты на C используют ее так все время. Это техническая деталь, для достижения которой вам нужно поставить знак «&» перед переменной. Это полностью отличается от Java, где это невозможно.
@fishinear: Опять же, вы не «передаете его как параметр». Установка & - это не «техническая деталь» - это самая важная деталь. Передача по ссылке - это очень технический термин, связанный с синтаксисом и семантикой. Что-то является «передачей по ссылке» только в том случае, если вы передаете переменную напрямую, без каких-либо дополнительных действий с ней. Если вы не хотите строго относиться к этим вещам, вам не следует использовать эти термины. Технически в C. НЕТ передачи по ссылке. Это хорошо известно и не оспаривается. Просто поищите в StackOverflow.
Хорошо, я сдаюсь. По-видимому, вы отказываетесь понимать, что означает концептуальная передача по ссылке и что это означает для программирования. По-видимому, вы также не видите, какая большая разница между C и Java. Мне вас жаль, это сильно ограничит ваше концептуальное мышление. Просто поищите в Интернете, и, надеюсь, вы увидите, что ваша точка зрения очень ограниченна. Удачи.
Здесь есть уже ответ, который объясняет ситуацию в PHP5.