Яка різниця між проходженням посилання та проходженням за значенням?


562

Яка різниця між

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

Чи можете ви надати мені кілька прикладів, будь ласка?



1
Якщо ви не знаєте, що таке адреса чи значення , дивіться тут
Honey

Відповіді:


1079

По-перше, відмінність "пропуск за величиною проти проходження посилання", визначена в теорії 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

"Автентичне" визначення, яке ви надаєте, не є визначенням, яке дається майже в кожному вступному курсі програмування. Google, що передається через посилання, і ви не отримаєте такої відповіді. Автентичне визначення, яке ви надаєте, - це неправильне використання посилання на слово, так як при дотриманні цього визначення ви використовуєте псевдонім, а не посилання: у вас є дві змінні, які насправді є однаковою змінною, тобто псевдонім, а не посилання. Ваше справжнє визначення викликає масову плутанину без причин. Просто скажіть, що пройти через посилання означає пропускну адресу. Це має сенс і уникне цього безглуздого плутанини.
YungGun

@ YungGun 1) Будь ласка, надайте посилання на "визначення, яке дається майже у кожному вступному курсі програмування". Також зауважте, що це має на меті бути зрозумілим у сучасних реаліях, а не в реаліях десятиліття чи трьох тому, коли були написані деякі курси CS. 2) "Адреса" не може бути використана у визначенні, оскільки вона свідомо абстрагується від можливих реалізацій. Наприклад, деякі мови (Fortran) не мають покажчиків; вони також відрізняються тим, чи піддають вони необроблену адресу користувачеві (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


9
чому потік? якщо щось не так або призводить до непорозумінь, залиште коментар.
Йоханнес Шауб - ліб

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

28
+1 для повноти. Не хвилюйтесь про нивотів - люди роблять це з дивних причин. У запитанні щодо калькуляторів всіх викликав хлопець, який не думав, що програмісти повинні використовувати калькулятори! У всякому разі, я вважав, що ваша відповідь була дуже хорошою.
Марк Бріттінгем

1
Посилання на пояснення Скіта порушені.
Програміст, орієнтований на гроші,

Посилання на пояснення Скіта досі порушені.
Rokit

85

Багато відповідей тут (і, зокрема, найбільш високообоснована відповідь) фактично невірні, оскільки неправильно розуміють, що насправді означає "дзвінок за посиланням". Ось моя спроба вирішити питання.

TL; DR

Простіше кажучи:

  • call by value означає, що ви передаєте значення як аргументи функції
  • Виклик за посиланням означає, що ви передаєте змінні як аргументи функції

У метафоричному відношенні:

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

Що "дзвінок за значенням" та "дзвінок за посиланням" не означають

Зауважимо, що обидва ці поняття є абсолютно незалежними та ортогональними від поняття посилальних типів (що на Java - це всі типи, що є підтипами Object, а у C # всіх classтипів) або від поняття типів вказівників, як у C (які семантично еквівалентні до "посилальних типів" Java, просто з різним синтаксисом).

Поняття типу посилання відповідає URL: воно є і самим інформаційним, і є посиланням ( покажчиком , якщо ви хочете) на іншу інформацію. Ви можете мати багато копій URL-адреси в різних місцях, і вони не змінюють веб-сайт, на який вони посилаються; якщо веб-сайт оновлюється, кожна копія URL-адреси все одно призведе до оновленої інформації. І навпаки, зміна URL-адреси в будь-якому одному місці не вплине на будь-яку іншу письмову копію URL-адреси.

Зауважте, що C ++ має поняття "посилання" (наприклад int&), яке не схоже на типи посилань Java та C # 's, але є як "виклик за посиланням". "Посилання на типи" Java та C # ', а також усі типи в Python, схожі на те, що C і C ++ називають "типами вказівників" (наприклад int*).


Гаразд, ось більш тривале та формальне пояснення.

Термінологія

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

Для початку, ось приклад дещо на мові С-подібної декларації функції:

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, також оголошений у рядку 1
  • arg- змінна , конкретно локальна змінна функції bar, оголошена та ініціалізована у рядку 2
  • argтакож є аргументом для конкретного виклику з fooв рядку 3

Тут слід виділити два дуже важливих набори понять. Перше - значення проти змінної :

  • Значення є результатом обчислення виразу на мові. Наприклад, у barфункції вище, після рядка int arg = 1;, вираз argмає значення 1 .
  • Мінлива є контейнером для значень . Змінна може бути змінною (це за замовчуванням у більшості мов, схожих на C), лише для читання (наприклад, оголошена за допомогою Java finalабо C # 's 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-адреси, не впливає на мою копію URL-адреси - це нас не хвилює (а насправді для таких мов, як Java та Python, "URL", або значення типу посилання, може не можна змінювати взагалі, тільки те, на що вона вказує) може.

Барбара Лісков, коли винайшла мову програмування CLU (яка мала цю семантику), зрозуміла, що існуючі терміни "дзвінок за значенням" та "виклик за посиланням" не є особливо корисними для опису семантики цієї нової мови. Тому вона придумала новий термін: дзвінок по обміну об'єктами .

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


Пояснено краще: тут слід виділити два дуже важливих набори понять. The first is value versus variable. The other important pair of concepts to distinguish is parameter versus argument:
СК Венкат

2
Відмінна відповідь. Я думаю, що хочу додати, що не потрібно створювати нове сховище у відповідь на посилання. Назва параметра посилається на оригінальне сховище (пам'ять).
Дякую

1
Найкраща відповідь ІМО
Рафаель Ейн

59

Перш ніж зрозуміти два терміни, Ви ОБОВ'ЯЗКОВО зрозуміти наступне. Кожен об’єкт має 2 речі, які можуть його розрізнити.

  • Його значення.
  • Його адреса.

Тож якщо ти скажеш employee.name = "John"

знайте, що є 2 речі про name. Його значення , яке , "John"а також його розташування в пам'яті , яка деякий шістнадцяткове число , може бути , як це: 0x7fd5d258dd00.

Залежно від архітектури мови або типу (класу, структури тощо) вашого об'єкта, ви будете або передавати, "John"або0x7fd5d258dd00

Проходження "John"відоме як проходження за значенням. Проходження 0x7fd5d258dd00відоме як проходження посиланням. Усі, хто вказує на це місце пам'яті, матимуть доступ до значення "John".

Для отримання додаткової інформації про це я рекомендую вам ознайомитися з перенаправленням покажчика, а також чому вибрати структуру (тип значення) над класом (тип посилання)


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 мені погано. так, ти правий. Дякуємо за виправлення
Taimoor Changaiz

28

Найпростіший спосіб отримати це у файлі Excel. Скажімо, наприклад, що у вас є два числа, 5 і 2 у комірках A1 і B1 відповідно, і ви хочете знайти їх суму в третій комірці, скажімо, A2. Зробити це можна двома способами.

  • Або передавши свої значення клітині A2 , набравши = 5 + 2 в цю клітинку. У цьому випадку, якщо значення комірок A1 або B1 змінюються, сума в A2 залишається такою ж.

  • Або передавши "посилання" комірок A1 і B1 на комірку A2 , набравши = A1 + B1 . У цьому випадку, якщо значення комірок A1 або B1 змінюються, змінюється також сума в A2.


Це найпростіший і найкращий приклад серед усіх інших відповідей.
Аміт Рей

18

Під час проходження посилання ви в основному передаєте вказівник на змінну. Передаючи значення, ви передаєте копію змінної. У базовому використанні це, як правило, означає прохідне зміна змінної, буде видно метод виклику і передавати значення, яке вони не мають.


12

Передача за значенням надсилає КОПІЮВАННЯ даних, що зберігаються у вказаній змінній, передача посилань посилає пряме посилання на саму змінну. Отже, якщо ви передасте змінну за посиланням, а потім зміните змінну всередині блоку, в який ви її передали, початкова змінна буде змінена. Якщо ви просто перейдете за значенням, оригінальну змінну не вдасться змінити блоком, в який ви її передали, але ви отримаєте копію того, що вона містилася під час виклику.


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

Тримайте це просто, заглядає. Стіни тексту можуть бути поганою звичкою.


Це дуже корисно для розуміння того, чи було змінено значення параметра чи ні, дякую!
Кевін Чжао

5

Основна різниця між ними полягає в тому, що змінні типу типу зберігають значення, тому, визначаючи змінну типу значення у виклику методу, передає копію значення цієї змінної методу. Змінні типу посилання зберігають посилання на об'єкти, тому, визначаючи змінну типу посилання в якості аргументу, передає методу копію фактичної посилання, що посилається на об'єкт. Незважаючи на те, що сама посилання передається за значенням, метод все ще може використовувати посилання, яке вона отримує, щоб взаємодіяти з - і, можливо, змінювати - вихідний об'єкт. Аналогічно, при поверненні інформації з методу через оператор return, метод повертає копію значення, що зберігається у змінній типу значення, або копію посилання, що зберігається у змінній типу посилання. Коли повертається посилання, метод виклику може використовувати це посилання для взаємодії з посилається об'єктом. Тому,

У c #, щоб передати змінну за посиланням, щоб названий метод міг модифікувати змінну, C # надає ключові слова ref і out. Застосування ключового слова ref до оголошення параметра дозволяє передати змінну методу за посиланням - викликаний метод зможе змінити оригінальну змінну в абонента. Ключове слово ref використовується для змінних, які вже ініціалізовані в методі виклику. Зазвичай, коли виклик методу містить неініціалізовану змінну як аргумент, компілятор генерує помилку. Попередньо параметр із ключовим словом виводить вихідний параметр. Це вказує компілятору, що аргумент буде переданий в викликаний метод за посиланням і що викликаний метод призначить значення вихідній змінній у виклику. Якщо метод не присвоює значення вихідному параметру в кожному можливому шляху виконання, компілятор генерує помилку. Це також заважає компілятору генерувати повідомлення про помилку для неініціалізованої змінної, яке передається як аргумент методу. Метод може повернути своєму абоненту лише одне значення через оператор return, але може повернути багато значень, вказавши параметри декількох вихідних (ref та / або out).

дивіться c # обговорення та приклади тут тексту посилання


3

Приклади:

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

const &як правило, найкраще. Ви не несете штрафу за будівництво та знищення. Якщо посилання не const, ваш інтерфейс передбачає, що він змінить передані дані.


2

Коротше кажучи, "Пройдене значення" - це ЩО воно є і передається за посиланням - ГДЕ ".

Якщо ваше значення VAR1 = "Щасливий хлопець!", Ви побачите лише "Щасливий хлопець!". Якщо VAR1 зміниться на "Happy Gal!", Ви цього не знаєте. Якщо це буде передано за посиланням, а VAR1 зміниться, ви.


2

Якщо ви не хочете змінювати значення вихідної змінної після передачі її у функцію, функція повинна бути побудована за допомогою параметра " передавати значення ".

Тоді функція матиме ТІЛЬКЕ значення, але не адресу переданої змінної. Без адреси змінної код всередині функції не може змінити значення змінної, як видно ззовні функції.

Але якщо ви хочете надати функції можливість змінювати значення змінної, як видно ззовні, вам потрібно скористатися пропуском за посиланням . Оскільки значення і адреса (посилання) передаються і доступні всередині функції.


1

передавати значення означає, як передавати значення функції за допомогою аргументів. мимобіжно за значенням ми копіюємо дані, що зберігаються у вказаній нами змінній, і це повільніше, ніж передавати за посиланням bcse t, коли дані копіюються. ми вносимо зміни в скопійовані дані, на вихідні дані це не впливає. Якщо ми передаємо посилання за адресою або за адресою, ми надсилаємо пряме посилання на саму змінну. або передача вказівника на змінну. це швидше, ніж менше часу витрачається


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: Ви також можете перевірити відповідь Ділана Бітті у поточній темі, яка пояснює це простими словами.


Ви заявляєте, "якщо параметр оголошено [як посилання], його відповідний фактичний параметр повинен бути змінною", але це взагалі не відповідає дійсності. Якщо посилання пов'язане з тимчасовим (наприклад, зворотним значенням функції), його термін служби збільшується, щоб відповідати посиланням. Детальніше дивіться тут .
Кріс Хант
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.