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

564

В чем разница между

  1. параметр, переданный по ссылке
  2. параметр передается по значению?

Не могли бы вы привести несколько примеров, пожалуйста?

Deduplicator
источник
1
Если вы не знаете, что такое адрес или значение, смотрите здесь
Honey

Ответы:

1081

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

Более новые языки 2, как правило, используют другую (но схожую) пару методов для достижения тех же результатов (см. Ниже), что является основным источником путаницы.

Вторым источником путаницы является тот факт, что в «передаче по ссылке» «ссылка» имеет более узкое значение, чем общий термин «ссылка» (потому что фраза предшествует этому).


Теперь, подлинное определение:

  • Когда параметр передается по ссылке , вызывающая сторона и вызываемая сторона используют одну и ту же переменную для параметра. Если вызываемый объект изменяет переменную параметра, эффект виден для переменной вызывающего.

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

Что следует отметить в этом определении:

  • «Переменная» здесь означает саму переменную вызывающего (локальную или глобальную) - то есть, если я передам локальную переменную по ссылке и назначу ее, я изменю саму переменную вызывающего, а не, например, на то, на что она указывает, если это указатель ,

    • Сейчас это считается плохой практикой (как неявная зависимость). Таким образом, практически все новые языки являются исключительно или почти исключительно передаваемыми по значению. Передача по ссылке теперь в основном используется в форме «аргументов вывода / ввода» в языках, где функция не может возвращать более одного значения.
  • Значение «ссылка» в «передать по ссылке» . Разница с общим термином «ссылка» заключается в том, что эта «ссылка» является временной и неявной. В сущности, вызываемый объект получает «переменную», которая как-то «совпадает» с исходной. То, как конкретно достигается этот эффект, не имеет значения (например, язык может также раскрывать некоторые детали реализации - адреса, указатели, разыменование - это все не имеет значения; если это чистый эффект, то это передача по ссылке).


Теперь в современных языках переменные имеют тенденцию быть «ссылочными типами» (другая концепция, изобретенная позже, чем «передача по ссылке» и вдохновленная ими), то есть фактические данные объекта хранятся где-то отдельно (обычно в куче), и только «ссылки» на него когда-либо хранятся в переменных и передаются как параметры. 3

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

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

Как вы можете видеть, эта пара техник почти такая же, как в определении, только с уровнем косвенности: просто замените «переменную» на «объект ссылки».

Для них нет согласованного имени, что приводит к искаженным объяснениям, таким как «вызов по значению, где значение является ссылкой». В 1975 году Барбара Лисков предложила термин « обмен вызовами по объектам » (или иногда просто «обмен вызовами по обмену»), хотя он так и не получил широкого распространения. Более того, ни одна из этих фраз не проводит параллели с оригинальной парой. Неудивительно, что старые термины в конечном итоге использовались в отсутствие чего-то лучшего, что приводило к путанице. 4


ПРИМЕЧАНИЕ : в течение длительного времени этот ответ говорил:

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

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

Это в основном правильно, за исключением более узкого значения «ссылка» - оно является как временным, так и неявным (это не обязательно, но быть явным и / или постоянным - это дополнительные функции, а не часть семантики передачи по ссылке) , как объяснено выше). Более близкая аналогия - дать вам копию документа и пригласить вас поработать над оригиналом.


1 Если вы не программируете на Fortran или Visual Basic, это не стандартное поведение, и в большинстве современных языков использование истинного вызова по ссылке даже невозможно.

2 Изрядное количество старших тоже поддерживают это

3 В нескольких современных языках все типы являются ссылочными типами. Этот подход был впервые введен языком CLU в 1975 году и с тех пор был принят многими другими языками, включая Python и Ruby. И многие другие языки используют гибридный подход, где некоторые типы являются «типами значений», а другие - «ссылочными типами» - среди них C #, Java и JavaScript.

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

ivan_pozdeev
источник
Лично я бы использовал термины «новый» или «косвенный» передача по значению / передача по ссылке для новых методов.
ivan_pozdeev
«Подлинное» определение, которое вы предоставляете, - это не определение, данное почти во всех вводных курсах программирования. Google, что передать по ссылке, и вы не получите этот ответ. Аутентичное определение, которое вы предоставляете, является неправильным использованием слова-ссылки, поскольку, когда вы следуете этому определению, вы используете псевдоним, а не ссылку: у вас есть две переменные, которые на самом деле являются одной и той же переменной, то есть псевдонимом, а не ссылкой. Ваше подлинное определение вызывает массовое замешательство без причины. Просто сказать, передать по ссылке означает передачу адреса. Это имеет смысл и позволит избежать этой бессмысленной путаницы.
YungGun
@YungGun 1) Пожалуйста, предоставьте ссылку на «определение, данное почти в каждом вводном курсе программирования». Также обратите внимание, что это имеет целью быть ясным в сегодняшних реалиях, а не в реалиях десятилетия или три назад, когда был написан некоторый курс по КС. 2) «Адрес» не может использоваться в определении, потому что он намеренно абстрагируется от возможных реализаций. Например, некоторые языки (фортран) не имеют указателей; они также различаются в том, предоставляют ли они необработанный адрес пользователю (VB не делает); это также не обязательно должен быть необработанный адрес памяти, что-либо, что позволило бы связать переменную.
ivan_pozdeev
@ivan_podeev нет ссылки извините. Я говорю «почти каждый вводный курс», потому что лично я учился в колледже и учился программированию буткемпов, что меня этому научило. Эти курсы были современными (менее 5 лет назад). «Необработанный адрес» является синонимом «указателя» ... Вы можете быть технически верным (согласно некоторой выбранной ссылке), но используемый вами язык непрактичен и запутан для большинства программистов. Если вы хотите , мои полные мысли об этом я уже писал в блоге в 3500 слов: medium.com/@isaaccway228/...
YungGun
@YungGun "слишком долго, не читал". Взгляд показывает точно путаницы, изложенные в ответе. Передача по ссылке является абстрактной техникой, не зависящей от реализации. Неважно, что именно передается под капот, важно, как это повлияет на программу.
ivan_pozdeev
150

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

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

коды

Так как мой язык - C ++, я буду использовать это здесь

// 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);
}

И пример на Java не повредит:

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;
    }
}

Википедия

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference

Этот парень в значительной степени прибивает это:

http://javadude.com/articles/passbyvalue.htm

Johannes Schaub - litb
источник
9
почему отрицание? если что-то не так или приводит к недоразумениям, пожалуйста, оставьте комментарий.
Йоханнес Шауб -
1
Это был не мой голос, но по второстепенному вопросу (вы знаете, тот, который обсуждали в президентских дебатах), я бы сказал, что это скорее «тактика», чем «стратегия».
Гарпо
28
+1 за полноту Не беспокойтесь о снижении голосов - люди делают это по странным причинам. В вопросе о калькуляторах все были отвергнуты парнем, который не думал, что программисты должны использовать калькуляторы! Во всяком случае, я думал, что ваш ответ был очень хорошим.
Марк Бриттингем
1
Ссылки на объяснения Скита не работают.
Программист, ориентированный на деньги
Ссылки на объяснения Скита до сих пор не работают.
Rokit
85

Многие ответы здесь (и, в частности, ответ с наибольшим количеством голосов) фактически неверны, так как они неправильно понимают, что на самом деле означает «вызов по ссылке». Вот моя попытка исправить ситуацию.

TL; DR

Проще говоря:

  • вызов по значению означает, что вы передаете значения в качестве аргументов функции
  • вызов по ссылке означает, что вы передаете переменные в качестве аргументов функции

В метафорических терминах:

  • Вызов по значению - это то, где я записываю что-то на листе бумаги и передаю вам . Может быть, это URL, может быть, это полная копия «Войны и мира». Неважно, что это, это на листе бумаги, который я вам дал, и теперь это фактически ваш лист бумаги . Теперь вы можете делать наброски на этом листе бумаги или использовать этот листок бумаги, чтобы найти что-то еще и поиграть с ним, что угодно.
  • Звонок по ссылке - это когда я даю тебе свой блокнот, в котором что-то записано . Вы можете писать в моей записной книжке (может быть, я этого хочу, а может и нет), а потом я сохраняю записную книжку с теми каракулями, которые вы там положили. Кроме того, если то, что вы или я написали, содержит информацию о том, как найти что-то в другом месте, вы или я можете пойти туда и поиграть с этой информацией.

Что не означает «звонок по значению» и «звонок по ссылке»

Обратите внимание, что обе эти концепции полностью независимы и ортогональны от концепции ссылочных типов (которая в Java - это все типы, которые являются подтипами Object, а в C # - все classтипы) или от концепции типов указателей, подобных C (которые семантически эквивалентны). на «ссылочные типы» Java, просто с другим синтаксисом).

Понятие ссылочного типа соответствует URL: оно само является частью информации и является ссылкой ( указатель , если хотите) на другую информацию. У вас может быть много копий URL в разных местах, и они не меняют сайт, на который они все ссылаются; если сайт обновляется, то каждая копия URL будет по-прежнему приводить к обновленной информации. И наоборот, изменение URL в любом месте не повлияет на любую другую письменную копию URL.

Обратите внимание , что C ++ есть понятие «ссылка» (например int&) , что является не как Java и C # 's „ссылочных типов“, но это как „вызов по ссылке“. «Ссылочные типы» в Java и C # и все типы в Python похожи на то, что в C и C ++ называют «типами указателей» (например int*).


Хорошо, вот более длинное и более формальное объяснение.

терминология

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

Для начала вот пример на некотором C-подобном языке объявления функции:

void foo(int param) {  // line 1
  param += 1;
}

И вот пример вызова этой функции:

void bar() {
  int arg = 1;  // line 2
  foo(arg);     // line 3
}

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

  • fooявляется функцией, объявленной в строке 1 (Java настаивает на создании всех методов функций, но концепция одинакова без потери общности; в C и C ++ проводится различие между объявлением и определением, которое я не буду здесь рассматривать)
  • paramявляется формальным параметр , чтобы foo, также объявлен в строке-
  • argявляется переменной , конкретно локальной переменной функции bar, объявленной и инициализирован в строке 2
  • argтакже является аргументом для конкретного вызова в fooстроке 3

Здесь следует выделить два очень важных набора понятий. Первый - это значение в зависимости от переменной :

  • Значение является результатом вычисления выражения на языке. Например, в приведенной barвыше функции после строки int arg = 1;выражение argимеет значение 1 .
  • Переменная является контейнером для значений . Переменная может быть изменяемой (это значение по умолчанию в большинстве C-подобных языков), доступна только для чтения (например, объявлена ​​с использованием Java finalили C # readonly) или может быть неизменной (например, с использованием C ++ const).

Другая важная пара понятий, которые следует различать, это параметр против аргумента :

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

Звонок по значению

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

Это работает точно так же, как любые другие виды переменных инициализируются значениями. Например:

int arg = 1;
int another_variable = arg;

Здесь argи another_variableесть полностью независимые переменные - их значения могут меняться независимо друг от друга. Тем не менее, в точке, где another_variableобъявлено, он инициализируется для хранения того же значения, которое argсодержит - что есть 1.

Поскольку они являются независимыми переменными, изменения another_variableне влияют на arg:

int arg = 1;
int another_variable = arg;
another_variable = 2;

assert arg == 1; // true
assert another_variable == 2; // true

Это точно так же, как отношения между argи paramв нашем примере выше, который я повторю здесь для симметрии:

void foo(int param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

Это точно так, как если бы мы написали код таким образом:

// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here

То есть определяющей характеристикой того, что означает вызов по значению, является то, что вызываемый объект ( fooв данном случае) получает значения в качестве аргументов, но имеет свои собственные отдельные переменные для этих значений из переменных вызывающего ( barв данном случае).

Возвращаясь к моей метафоре выше, если я barи ты foo, когда я звоню тебе, я вручаю вам кусок бумаги с значением , написанным на ней. Вы называете этот листок бумаги param. Это значение является копией значения, которое я написал в своей записной книжке (мои локальные переменные) в переменной, которую я вызываю arg.

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

Звоните по ссылке

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

Возвращаясь к нашему примеру выше, это эквивалентно:

// 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

Поскольку paramэто просто другое имя для arg- то есть они являются одной и той же переменной , изменения paramотражаются в arg. Это основной способ, которым вызов по ссылке отличается от вызова по значению.

Очень немногие языки поддерживают вызов по ссылке, но C ++ может сделать это так:

void foo(int& param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

В этом случае, paramне только имеют такую же ценность , как arg, на самом деле является arg (только под другим именем) , и поэтому barможно заметить , что argбыло увеличено.

Обратите внимание, что это не так, как работает сегодня любой из Java, JavaScript, C, Objective-C, Python или почти любой другой популярный язык. Это означает, что эти языки не называются по ссылке, они называются по значению.

Приложение: вызов по обмену объектами

Если то, что у вас есть, это вызов по значению , но фактическое значение является ссылочным типом или типом указателя , то само «значение» не очень интересно (например, в C это просто целое число, зависящее от платформы) - что интересно то, на что указывает это значение .

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

Снова заимствуя аналогию с URL, тот факт, что я дал вам копию URL для веб-сайта, не особенно интересен, если нас обоих волнует не веб-сайт, а веб-сайт. Тот факт, что вы перебираете свою копию URL-адреса, не влияет на мою копию URL-адреса, нас не волнует (и на самом деле, в таких языках, как Java и Python, «URL», или значение ссылочного типа, может не может быть изменен, может только указанная им вещь).

Барбара Лисков, когда она изобрела язык программирования CLU (который имел эту семантику), поняла, что существующие термины «вызов по значению» и «вызов по ссылке» не особенно полезны для описания семантики этого нового языка. Таким образом, она изобрела новый термин: вызов через разделение объектов .

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

Даниэль Приден
источник
Объяснено лучше: есть два очень важных набора понятий, которые следует различать. The first is value versus variable. The other important pair of concepts to distinguish is parameter versus argument:
СК Венкат
2
Отличный ответ. Я думаю, что я бы добавил, что не нужно создавать новое хранилище при передаче по ссылке. Имя параметра ссылается на оригинальное хранилище (память). Спасибо
drlolly
1
Лучший ответ ИМО
Рафаэль Эйнг
59

Прежде чем понимать 2 условия, вы ДОЛЖНЫ понять следующее. У каждого объекта есть 2 вещи, которые могут его выделить.

  • Его ценность.
  • Его адрес.

Так что если вы скажете employee.name = "John"

знаю, что есть 2 вещи о name. Его значение , которое , "John"а также его расположение в памяти , которая некоторое шестнадцатеричное число , может быть , как это: 0x7fd5d258dd00.

В зависимости от архитектуры языка или типа (класса, структуры и т. Д.) Вашего объекта, вы будете либо передавать, "John"либо0x7fd5d258dd00

Передача "John"называется передачей по значению. Передача 0x7fd5d258dd00называется передачей по ссылке. Любой, кто указывает на эту область памяти, будет иметь доступ к значению "John".

Более подробно об этом я рекомендую вам прочитать о разыменовании указателя, а также о том, почему следует выбирать struct (тип значения) вместо класса (ссылочный тип)

Мед
источник
3
То есть я искал, на самом деле нужно искать концепцию, а не просто объяснение, спасибо.
Haisum Usman
Java всегда передается по значению. Передача ссылок на объекты в Java считается передачей по значению. Это противоречит вашему утверждению «Передача 0x7fd5d258dd00 называется передачей по ссылке».
Четан
53

Вот пример:

#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
}
Пен
источник
1
Я думаю, что есть одна проблема, так как последняя строка должна печатать 0 вместо 2. Пожалуйста, скажите мне, если я что-то упустил.
Таймур Чангайз
@TaimoorChangaiz; Какая "последняя строка"? Кстати, если вы можете использовать IRC, зайдите на ## программирование на Freenode. Там было бы намного легче объяснить вещи. Мой ник там есть "Пион".
пион
1
@ EduardoLeón by_val (y); std :: cout << y << std :: endl; // печатает 2
Таймур Чангайз
5
@TaimoorChangaiz: почему бы не напечатать 2? yуже было установлено на 2 предыдущей строкой. Почему бы вернуться к 0?
Пион
@ EduardoLeón мой плохой. да ты прав. Спасибо за исправление
Таймур Чангайз
28

Самый простой способ получить это - файл Excel. Скажем, например, что у вас есть два числа, 5 и 2 в ячейках A1 и B1 соответственно, и вы хотите найти их сумму в третьей ячейке, скажем, A2. Вы можете сделать это двумя способами.

  • Либо, передав их значения в ячейку A2 , введя = 5 + 2 в эту ячейку. В этом случае, если значения ячеек A1 или B1 изменяются, сумма в A2 остается неизменной.

  • Или передавая «ссылки» ячеек A1 и B1 в ячейку A2 , набрав = A1 + B1 . В этом случае, если значения ячеек A1 или B1 изменяются, сумма в A2 также изменяется.

Чем скуртан
источник
Это самый простой и лучший пример среди всех остальных ответов.
Амит Рэй
18

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

Craig
источник
12

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

MetaGuru
источник
7

Передача по значению - функция копирует переменную и работает с копией (поэтому она ничего не меняет в исходной переменной)

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

Пример (скопируйте и используйте / попробуйте сами и посмотрите):

#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;
}

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

user326964
источник
Это действительно помогает понять, изменилось ли значение параметра или нет, спасибо!
Кевин Чжао
5

Основное различие между ними заключается в том, что переменные типа значения хранят значения, поэтому указание переменной типа значения в вызове метода передает копию значения этой переменной в метод. Переменные ссылочного типа хранят ссылки на объекты, поэтому указание переменной ссылочного типа в качестве аргумента передает методу копию фактической ссылки, которая ссылается на объект. Даже если сама ссылка передается по значению, метод может использовать полученную ссылку для взаимодействия с исходным объектом и, возможно, для его изменения. Аналогично, при возврате информации из метода через оператор return метод возвращает копию значения, хранящегося в переменной типа значения, или копию ссылки, хранящейся в переменной типа ссылки. Когда ссылка возвращается, вызывающий метод может использовать эту ссылку для взаимодействия с указанным объектом. Так,

В c # для передачи переменной по ссылке, чтобы вызываемый метод мог изменять переменные, C # предоставляет ключевые слова ref и out. Применение ключевого слова ref к объявлению параметра позволяет передавать переменную в метод по ссылке - вызываемый метод сможет изменять исходную переменную в вызывающей стороне. Ключевое слово ref используется для переменных, которые уже были инициализированы в вызывающем методе. Обычно, когда вызов метода содержит неинициализированную переменную в качестве аргумента, компилятор генерирует ошибку. Предшествующий параметр с ключевым словом out создает выходной параметр. Это указывает компилятору, что аргумент будет передан в вызываемый метод по ссылке и что вызываемый метод назначит значение исходной переменной в вызывающей программе. Если метод не присваивает значение выходному параметру на каждом возможном пути выполнения, компилятор генерирует ошибку. Это также препятствует тому, чтобы компилятор генерировал сообщение об ошибке для неинициализированной переменной, которая передается в качестве аргумента методу. Метод может возвратить вызывающей стороне только одно значение с помощью оператора return, но может вернуть много значений, указав несколько выходных (ref и / или out) параметров.

см. обсуждение c # и примеры здесь, текст ссылки

Тина Эндресен
источник
3

Примеры:

class Dog 
{ 
public:
    barkAt( const std::string& pOtherDog ); // const reference
    barkAt( std::string pOtherDog ); // value
};

const &как правило, лучше. Вы не несете штраф за строительство и разрушение. Если ссылка не является константой, ваш интерфейс предполагает, что она изменит переданные данные.

Deduplicator
источник
2

Короче говоря, Passed by value - это то, ЧТО это и передается по ссылке, ГДЕ это.

Если ваше значение VAR1 = «Счастливый парень!», Вы увидите только «Счастливый парень!». Если VAR1 изменится на «Happy Gal!», Вы об этом не узнаете. Если он передается по ссылке, а VAR1 изменяется, вы это сделаете.

монстр
источник
2

Если вы не хотите изменять значение исходной переменной после ее передачи в функцию, функция должна быть построена с параметром « pass by value ».

Тогда функция будет иметь ТОЛЬКО значение, но не адрес переданной переменной. Без адреса переменной код внутри функции не может изменить значение переменной, как видно снаружи функции.

Но если вы хотите дать функции возможность изменять значение переменной, как видно снаружи, вам нужно использовать передачу по ссылке . Поскольку значение и адрес (ссылка) передаются и доступны внутри функции.

Стэнли
источник
1

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

абхиниша такур
источник
0

Вот пример, который демонстрирует различия между передачей по значению - значением указателя - ссылкой :

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
}

Метод «передачи по ссылке» имеет важное ограничение . Если параметр объявлен как переданный по ссылке (поэтому ему предшествует знак &), его соответствующий фактический параметр должен быть переменной .

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

Функция не может поместить значение в нечто иное, чем переменная. Он не может присвоить литералу новое значение или заставить выражение изменить его результат.

PS: Вы также можете проверить ответ Дилана Битти в текущей ветке, которая объясняет его простыми словами.

BugShotGG
источник
Вы заявляете «если параметр объявлен [в качестве ссылки], его соответствующий фактический параметр должен быть переменной», но это не так в целом. Если ссылка привязана к временному объекту (например, возвращаемому значению функции), срок ее службы увеличивается, чтобы соответствовать ссылке. Смотрите здесь для деталей.
Крис Хант