При представлении ссылка всегда является значением, независимо от того, какой язык вы используете.
Получив внешний вид коробки, давайте посмотрим на сборку или некоторое низкоуровневое управление памятью. На уровне ЦП ссылка на что-либо немедленно становится значением , если оно записывается в память или в один из регистров ЦП. (Вот почему указатель является хорошим определением. Это значение, которое одновременно имеет цель).
Данные в памяти имеют Местоположение , и в этом месте есть значение (байт, слово, что угодно). В ассемблере у нас есть удобное решение: Name для определенного Location (она же переменная), но при компиляции кода ассемблер просто заменяет Name на указанное местоположение так же, как ваш браузер заменяет доменные имена на IP-адреса.
Вплоть до сути технически невозможно передать ссылку на что-либо на любом языке, не представляя его (когда оно сразу становится значением).
Допустим, у нас есть переменная Foo, ее Местоположение находится на 47-м байте в памяти, а ее Значение равно 5. У нас есть другая переменная Ref2Foo , которая находится в памяти в 223-м байте, и его значение будет 47. Этот Ref2Foo может быть технической переменной, явно не созданной программой. Если вы просто посмотрите на 5 и 47 без какой-либо другой информации, вы увидите только два значения .
Если вы используете их в качестве ссылок, чтобы добраться до 5
, мы должны путешествовать:
(Name)[Location] -> [Value at the Location]
---------------------
(Ref2Foo)[223] -> 47
(Foo)[47] -> 5
Вот как работают таблицы переходов.
Если мы хотим вызвать метод / функцию / процедуру со значением Foo, существует несколько возможных способов передачи переменной в метод, в зависимости от языка и нескольких режимов вызова его метода:
- 5 копируется в один из регистров ЦП (т. Е. EAX).
- 5 возвращает PUSHd в стек.
- 47 копируется в один из регистров процессора
- 47 PUSHd в стек.
- 223 копируется в один из регистров ЦП.
- 223 возвращает PUSHd в стек.
В каждом случае выше указанного значения - копия существующего значения - было создано, теперь его должен обработать метод получения. Когда вы пишете «Foo» внутри метода, он либо считывается из EAX, либо автоматически разыменовывается , либо дважды разыменовывается, процесс зависит от того, как работает язык и / или от того, что диктует тип Foo. Это скрыто от разработчика, пока она не обходит процесс разыменования. Таким образом, ссылка - это значение , когда оно представлено, потому что ссылка - это значение, которое должно быть обработано (на уровне языка).
Теперь мы передали Foo методу:
- в случае 1. и 2. если вы измените Foo (
Foo = 9
), это повлияет только на локальную область, поскольку у вас есть копия значения. Изнутри метода мы даже не можем определить, где в памяти находился оригинальный Foo.
- в случаях 3. и 4. Если вы используете языковые конструкции по умолчанию и изменяете Foo (
Foo = 11
), это может изменить Foo глобально (зависит от языка, т. Е. Java или как у Паскаля procedure findMin(x, y, z: integer;
var m : integer);
). Однако, если язык позволяет обойти процесс разыменования, вы можете изменить 47
, скажем на 49
. В этот момент, кажется, Foo изменился, если вы прочитали его, потому что вы изменили локальный указатель на него. И если вы захотите изменить этот Foo внутри метода (Foo = 12
), вы, вероятно, откажетесь от выполнения программы (иначе, от segfault), потому что вы будете писать в память, отличную от ожидаемой, вы даже можете изменить область, предназначенную для этого. хранение исполняемой программы и ее запись изменят выполняемый код (Foo теперь не в 47
). НО значение Foo 47
не изменилось глобально, только значение внутри метода, потому что 47
также было копией метода. - в случаях 5. и 6. Если вы измените
223
внутри метода, он создаст тот же хаос, что и в 3. или 4. (указатель, указывающий на неверное значение, которое снова используется в качестве указателя) но это все еще локальная проблема, так как 223 было скопировано . Однако, если вы можете разыменовать Ref2Foo
(то есть 223
), достичь и изменить указанное значение 47
, скажем, на 49
, это повлияет на Foo глобально , потому что в в этом случае методы получили копию 223
, но ссылка 47
существует только один раз, и изменение ее на 49
приведет к тому, что каждая Ref2Foo
двойная разыменование приведет к неправильному значению.
Проницание незначительными деталями, даже языки, которые передают по ссылке, будут передавать значения в функции, но эти функции знают, что они должны использовать это для разыменования. Эта передача-ссылка-как-значение просто скрыта от программиста, потому что она практически бесполезна, а терминология - только передача по ссылке .
Strict передача по значению также бесполезна, это будет означать, что 100-мегабайтный массив должен копироваться каждый раз, когда мы вызываем метод с массивом в качестве аргумента, поэтому Java не может быть строго передана -по-значение. Каждый язык передает ссылку на этот огромный массив (в качестве значения) и использует механизм копирования при записи, если этот массив может быть изменен локально внутри метода, или позволяет методу (как это делает Java) изменять массив глобально (из представление вызывающего абонента) и несколько языков позволяет изменять значение самой ссылки.
Таким образом, вкратце и в собственной терминологии Java, Java это передача по значению , где значение может быть: либо действительное значение , либо значение , которое является представлением ссылки .