В чем разница между передачей по ссылке в Java и передачей указателя в C? - PullRequest
15 голосов
/ 02 марта 2009

Я изучаю Java несколько месяцев и сейчас начинаю изучать C.

Я немного растерялся, у меня сложилось впечатление, что передача объекта по ссылке и передача указателя на этот объект - это одно и то же: я думал, что разница в том, что в Java вся передача объектов выполняется с помощью указателей автоматически где, как и в C, нужно и тут и там разбрызгивать маленькие звездочки и амперсанды. Недавно в разговоре меня уверили, что разница есть!

В чем разница между передачей по ссылке и передачей указателя?

Ответы [ 10 ]

39 голосов
/ 02 марта 2009

Ни Java , ни C не имеют передачи по ссылке. Они оба строго передаются по значению.

Семантика передачи по ссылке означает, что при изменении значения параметра в методе вызывающая сторона увидит это изменение в аргументе.

Теперь вы можете подумать: «Но это так в Java! Если я изменю объект во время метода, вызывающая сторона увидит это изменение». Объект не является параметром. Параметром является просто переменная - и если вы измените значение этой переменной, вызывающая сторона не увидит этого. Например:

public void foo(Object x)
{
    x = null;
}

public void caller()
{
    Object y = new Object();
    foo(y);
    // y is still not null!
}

Если бы параметр был действительно , переданный по ссылке, y впоследствии был бы нулевым. Вместо этого значение y является просто ссылкой, и эта ссылка передается по значению. Это сбивает с толку, потому что слово «ссылка» в обоих терминах, но это разные вещи.

Вы можете посмотреть мою статью о передаче параметров C # , чтобы увидеть, что было бы возможно, если бы у Java была семантика передачи по ссылке, как в C #, когда (и только когда) вы используете ключевое слово ref.

Вы также можете посмотреть комментарии к моему ответу переполнения стека , относящемуся к теме.

7 голосов
/ 02 марта 2009

Я думал, что разница в том, что в Java вся передача объектов выполняется с помощью указателей автоматически, тогда как, как и в С, нужно на самом деле разбрызгивать маленькие звездочки и амперсанды здесь и там.

Концептуально, это совершенно верно. Если мы педантичны (и это хорошо), мы можем даже сказать, что объекты вообще не передаются в Java. Передается только «указатель», который в Java называется ссылкой. Все косвенные ссылки делаются автоматически. Поэтому, когда вы делаете «objref-> foo» вместо «objref.foo» в C и не можете использовать точку, потому что работаете с указателем, в Java вы все равно можете использовать точку, потому что она ничего не знает для доступ к членам в любом случае. В C вы можете передать указатель (а здесь это фактически называется указателем) и вы можете передать сам объект, и в этом случае копия передается. В C вы получаете доступ к объекту, на который указывает указатель, путем косвенного обращения, используя звездочку или «->». В Java единственный доступ к объектам в любом случае - использование точки (objref.member).

Если мы снова педантичны (теперь снова важнее), ни в Java, ни в C нет «передачи по ссылке». В Java мы передаем ссылку / указатель на объект, а в C мы либо передаем копию объекта (очевидный случай), либо снова передаем просто копию указателя на объект. Таким образом, в обоих случаях - Java и C - мы передаем адреса объектов. Не говоря о примитивных типах java, которые копируются как в Java, так и в C - даже если вы можете передать их адрес также в C, нет никакой разницы между агрегатным типом (т.е. структурой) и «примитивным типом» в C в это отношение.

3 голосов
/ 02 марта 2009

Различия невелики. На уровне сборки в обоих случаях адрес объекта передается функции. Однако в случае указателя параметр является независимым указателем, который можно использовать для изменения целевого объекта (* pch = 'x') или для доступа к другому объекту (pch = "newstr"). В ссылочном случае параметр автоматически перенаправляется на целевой объект.

2 голосов
/ 02 марта 2009

True передача по ссылке означает, что вы можете присвоить ссылке новое значение, и это новое значение будет видно в контексте вызова так же, как внутри вызываемого метода.

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

2 голосов
/ 02 марта 2009

Я считаю, что указатель - это переменная, которая содержит ссылку. С помощью & вы можете получить направление памяти (ссылка), а с помощью * вы можете получить доступ к содержимому направления памяти. Предлагаю книгу Язык программирования C .

Привет

2 голосов
/ 02 марта 2009

Несколько вещей

  • C не имеет ссылок (несмотря на C ++)
  • У Java нет указателей
  • Разница между указателями и ссылками в том, что указатели - это практически адреса памяти, с которыми вы можете рассчитывать. Ссылки не могут быть рассчитаны с
  • Java и C передают по значению (при передаче объекта в Java ссылка копируется в функцию)
1 голос
/ 08 июля 2011

Те читатели, которые знакомы с C / C ++, вероятно, заметили, что появляются ссылки на объекты быть похожим на указатели. Это подозрение, по сути, правильно. Ссылка на объект похож на указатель памяти. Основное отличие - и ключ к безопасности Java - в том, что вы не можете манипулировать ссылками, как вы можете фактические указатели. Таким образом, вы не можете вызвать ссылка на объект, указывающая на произвольную ячейку памяти или манипулирующая им как целое число.

  • Java не поддерживается и оператор, тогда как вы можете получить адрес объекта в Java.
  • В Java ссылка делается на объект, например

MyClass object1 = new MyClass ();

MyClass object2 = object1;

т.е. Вы можете подумать, что object1 и object2 ссылаются на отдельные и разные объекты. Однако это было бы неправильно. Вместо этого после выполнения этого фрагмента, object1 и object2 будут ссылаться на один и тот же объект. если вы измените в любом другом эффекте.

* вы не можете изменить адрес объекта после инициализации т.е. MyClass obj = new MyClass (); как ссылка в C ++, с объектом вы можете изменить только значение экземпляра объекта

Примечание: Java предоставляет специальную функцию object1 был установлен в null, но object2 по-прежнему указывает на исходный объект.

1 голос
/ 02 марта 2009

Об этом недавно вышла статья Эрика Липперта. Он программист на компиляторе C #, но все, что он говорит, имеет достаточно общего, чтобы распространиться на другие языки GC, такие как Java:

http://blogs.msdn.com/ericlippert/archive/2009/02/17/references-are-not-addresses.aspx

Цитата из статьи:

Указатели строго «более мощные», чем ссылки; все, что вы можете сделать со ссылками, вы можете сделать с указателями. [...]

Указатели обычно реализуются как адреса. Адрес - это число, которое является смещением в «массив байтов», который является всем виртуальным адресным пространством процесса. [...]

По всем этим причинам мы не описываем ссылки как адреса в спецификации. Спецификация просто говорит, что переменная ссылочного типа «хранит ссылку» на объект и оставляет ее совершенно неопределенной относительно того, как это может быть реализовано. Точно так же переменная указателя хранит «адрес» объекта, который, опять же, остается довольно расплывчатым. Нигде мы не говорим, что ссылки совпадают с адресами.

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

В отличие от ссылки, вы можете сделать гораздо больше с указателем, содержащим адрес. Адреса можно манипулировать математически; вы можете вычесть одно из другого, вы можете добавить к ним целые числа и так далее. Их законные операции указывают, что они являются «причудливыми числами», которые индексируют в «массив», который является виртуальным адресным пространством процесса.

1 голос
/ 02 марта 2009

Если вы изучаете C (а не C ++), то ссылок нет.

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

Во многих отношениях это похоже на то, что вы видите в Java, за исключением того, что в Java вам не нужно явно превращать объекты в указатель или разыменовывать их. Другими словами, когда вы передаете указатель, адрес копируется в стек, поэтому, если ваша функция изменяет указатель (а не данные, на которые она указывает), эти изменения исчезают, когда вы закончите с функцией. Точно так же, когда вы передаете ссылку на объект в Java, вы можете изменить содержимое объекта (например, путем вызова функций), но изменение varialbe для указания на другой объект не будет иметь никакого эффекта после вашего выхода.

Если вы использовали C ++ (который выглядит как C), то вы можете передавать по ссылке, в этом случае вам не нужно иметь дело с указателями, и изменения в переменной в функции фактически изменяют внешнее значение напрямую ( за исключением того, что вы не можете указать это на что-то еще).

0 голосов
/ 02 марта 2009

Я не уверен насчет Java, но в C # или VB.net вы можете передать по значению или по ссылке

пример, передаваемый по ссылке

    static void Main(string[] args)
    {
        int x = 1;
        Foo(ref x);
        Console.WriteLine(x.ToString());//this will print 2
    }

    private static void Foo(ref int x)
    {
        x++;
    }

обратите внимание, что x равен 1, но когда вы вызываете метод Foo и передаете x по ссылке, когда x увеличивается на единицу в Foo, исходное значение также увеличивается

...