Referans ile geçiş ile değere göre geçiş arasındaki fark nedir?


Yanıtlar:


1079

İlk ve en önemlisi, CS teorisinde tanımlanan "değere göre geçişle referans ile geçiş" ayrımı artık geçersizdir çünkü başlangıçta "referansla geçiş" olarak tanımlanan teknik o zamandan beri lehte düşmüştür ve nadiren kullanılmaktadır. 1

Daha yeni diller 2 , birincil karışıklık kaynağı olan aynı etkileri (aşağıya bakınız) elde etmek için farklı (ama benzer) bir çift teknik kullanma eğilimindedir .

İkincil bir karışıklık kaynağı , "referans ile geç" te, "referans" ın, "referans" genel teriminden daha dar bir anlama sahip olmasıdır (cümle daha önce gelir).


Şimdi, otantik tanım:

  • Bir parametre referans olarak iletildiğinde , arayan ve arayan parametre için aynı değişkeni kullanır . Callee parametre değişkenini değiştirirse, efekt arayanın değişkeni tarafından görülebilir.

  • Bir parametre değere göre iletildiğinde , arayan ve arayan aynı değerde iki bağımsız değişkene sahiptir. Callee parametre değişkenini değiştirirse, efekt arayan tarafından görülemez.

Bu tanımda dikkat edilmesi gerekenler:

  • "Değişken" burada arayanın (yerel veya global) değişkeninin kendisi anlamına gelir - yani yerel bir değişkeni referans olarak iletir ve ona atarsam, arayanın değişkenini değiştiririm, örneğin bir işaretçi ise işaret ettiği her şeyi değil .

    • Bu artık kötü uygulama olarak kabul edilmektedir (üstü kapalı bir bağımlılık olarak). Bu nedenle, hemen hemen tüm yeni diller yalnızca veya neredeyse tamamen değere sahiptir. Doğrudan başvuru, bir işlevin birden fazla değer döndüremediği dillerde artık "çıktı / girdi bağımsız değişkenleri" biçiminde kullanılmaktadır.
  • "Referansla geç" içindeki "referans" ın anlamı . Genel "referans" terimi ile fark, bu "referans" ın geçici ve örtük olmasıdır. Callee'nin temelde aldığı şey, bir şekilde orijinal olanla aynı olan bir "değişken" tir . Bu etkinin spesifik olarak nasıl elde edildiği önemsizdir (örneğin, dil bazı uygulama ayrıntılarını da gösterebilir - adresler, işaretçiler, kayıttan çıkarılma - bunların hepsi önemsizdir; eğer net etki bu ise, referans yoluyla).


Şimdi, modern dillerde, değişkenler "referans türleri" olma eğilimindedir ("referans ile geç" den sonra icat edilmiş ve esinlenerek oluşturulmuş başka bir kavram), yani gerçek nesne verileri ayrı bir yerde (genellikle yığın üzerinde) saklanır ve sadece "referanslar" değişkenlerde tutulur ve parametre olarak iletilir. 3

Böyle bir referansı geçmek, değerin altına düşer, çünkü bir değişkenin değeri teknik olarak referans nesnedir, referanslanan nesne değildir. Bununla birlikte, program üzerindeki net etki, değere göre ya da başvuruya göre aynı olabilir:

  • Bir başvuru, yalnızca arayanın değişkeninden alınır ve bağımsız değişken olarak iletilirse, bu, referansla geçişle aynı etkiye sahiptir: atıfta bulunulan nesne, çağrıda mutasyona uğratılırsa, arayan değişikliği görür.
    • Bununla birlikte, bu referansı tutan bir değişken yeniden sınanırsa, o nesneyi işaret etmeyi durduracaktır, bu nedenle bu değişken üzerindeki diğer işlemler, şimdi işaret ettiği şeyi etkileyecektir.
  • Geçiş değeriyle aynı etkiye sahip olmak için bir noktada nesnenin bir kopyası oluşturulur. Seçenekler şunları içerir:
    • Arayan, aramadan önce özel bir kopya oluşturabilir ve arayan kişiye bunun yerine bir referans verebilir.
    • Bazı dillerde, bazı nesne türleri "değişmez" dir: üzerlerinde değeri değiştiren herhangi bir işlem aslında orijinal olanı etkilemeden tamamen yeni bir nesne oluşturur. Bu nedenle, bu tür bir nesnenin argüman olarak iletilmesi her zaman pass-by-etkisi etkisine sahiptir: callee için bir değişiklik gerektiğinde ve gerektiğinde otomatik olarak bir kopya oluşturulur ve arayanın nesnesi asla etkilenmez.
      • İşlevsel dillerde, tüm nesneler değişmezdir.

Gördüğünüz gibi, bu teknik çifti tanımdaki tekniklerle hemen hemen aynıdır, sadece dolaylı bir düzeydedir: sadece "değişken" i "başvurulan nesne" ile değiştirin.

Onlar için üzerinde anlaşmaya varılmış bir isim yoktur, bu da "değerin referans olduğu değere göre çağrı" gibi çarpık açıklamalara yol açar. 1975 yılında Barbara Liskov, "tamamen nesne paylaşma çağrısı" (ya da bazen sadece "paylaşma çağrısı") terimini önermedi . Dahası, bu cümlelerin hiçbiri orijinal çiftle paralellik göstermez. Eski terimlerin daha iyi bir şeyin yokluğunda yeniden kullanılmasına ve şaşkınlığa yol açmasına şaşmamalı. 4


NOT : Uzun zamandır bu cevap şöyle derdi:

Diyelim ki bir web sayfasını sizinle paylaşmak istiyorum. Size URL'yi söylersem, referansla geçiyorum. Gördüğüm aynı web sayfasını görmek için bu URL'yi kullanabilirsiniz. Bu sayfa değiştirilirse, ikimiz de değişiklikleri görürüz. URL'yi silerseniz, yaptığınız tek şey o sayfaya olan başvurunuzu yok etmektir; gerçek sayfanın kendisini silmiyorsunuzdur.

Sayfayı yazdırıp çıktısını verirsem, değere göre geçiyorum. Sayfanız orijinalin bağlantısı kesilmiş bir kopyası. Sonraki değişiklikleri görmezsiniz ve yaptığınız değişiklikler (örneğin, çıktıda karalama) orijinal sayfada görünmez. Çıktıyı imha ederseniz, aslında nesnenin kopyanızı imha ettiniz - ancak orijinal web sayfası olduğu gibi kalır.

Bu "referans" ın daha dar anlamı dışında çoğunlukla doğrudur - hem geçici hem de örtük (zorunlu değildir, ancak açık ve / veya kalıcı olması, referans by-pass semantiğinin bir parçası değildir, ek özelliklerdir , yukarıda açıklandığı gibi). Daha yakın bir benzetme, bir belgenin bir kopyasını size orijinal üzerinde çalışmaya davet etmekle karşılaştırır.


1 Fortran veya Visual Basic'te programlama yapmadığınız sürece, bu varsayılan davranış değildir ve modern kullanımdaki çoğu dilde, gerçek referans çağrı bile mümkün değildir.

2 Oldukça fazla yaşlı olanlar da bunu destekliyor

3 Birkaç modern dilde, tüm türler referans türleridir. Bu yaklaşıma 1975 yılında CLU dili öncülük etmiştir ve o zamandan beri Python ve Ruby dahil olmak üzere birçok dil tarafından benimsenmiştir. Ve daha birçok dilde, bazı türlerin "değer türleri" ve diğerlerinin "referans türleri" olduğu karma aralıklar kullanılır; bunlar arasında C #, Java ve JavaScript bulunur.

4 Eski bir terimin uygun bir şekilde geri dönüştürülmesinde kötü bir şey yoktur , ancak her seferinde hangi anlamın kullanıldığını bir şekilde açıklığa kavuşturmak gerekir. Bunu yapmamak, tam olarak karışıklığa neden olan şeydir.


Şahsen yeni teknikler için "yeni" veya "dolaylı" değer bazında / referans bazında terimlerini kullanacağım .
ivan_pozdeev

Sağladığınız “özgün” tanım, hemen hemen her tanıtım programlama dersinde verilen tanım değildir. Google ne referans ile geçmek ve bu cevap alamazsınız. Sağladığınız gerçek tanım, başvuruyu değil, diğer adı kullandığınız bir tanımı kullandığınızda olduğu gibi, başvuru kelimesinin yanlış kullanılmasıdır : aslında aynı değişken olan iki değişkeniniz var, bu bir başvuru değil. Otantik tanımınız hiçbir sebep olmadan kitlesel karışıklığa neden olur. Sadece referansla geçmenin adresi geçmesi anlamına geldiğini söyleyin. Bu mantıklı ve bu anlamsız karışıklıktan kaçınacaktı.
YungGun

@YungGun 1) Lütfen "hemen hemen her tanıtım programlama dersinde verilen tanım" bağlantısını belirtin. Ayrıca bunun, bir CS dersinin yazıldığı on yıl veya üç yıl önce değil, bugünün gerçeklerinde açık olmayı amaçladığını unutmayın. 2) "Adres" tanım olarak kullanılamaz, çünkü olası uygulamalardan kasıtlı olarak soyutlanır. Örneğin bazı dillerin (Fortran) işaretçileri yoktur; aynı zamanda ham adresi kullanıcıya ifşa etmeleri konusunda da farklılık gösterirler (VB bunu yapmaz); aynı zamanda bir ham bellek adresi olmak zorunda değildir, değişkene bağlantı sağlayacak herhangi bir şey yapacaktır.
ivan_pozdeev

@ivan_podeev bağlantı yok özür dilerim. "Hemen hemen her tanıtım dersi" diyorum çünkü bizzat üniversiteye gittim ve programlama öğretici kampları da aldım. Bu kurslar moderndi (5 yıldan az bir süre önce). "Ham adres", "işaretçi" ile eşanlamlıdır ... Teknik olarak doğru olabilirsiniz (bazı kiraz bağlantısına göre), ancak kullandığınız dil çoğu programcı için pratik değildir ve kafa karıştırıcıdır. Eğer tam düşüncelerimi istiyorsan 3500 kelimelik bir blog yazısı yazdım
YungGun

@GungGun "çok uzun, okumadım". Bir bakışta yanıtta özetlenen karışıklıklar tam olarak gösterilir. Referans yoluyla geçme, uygulamaya agnostik soyut bir tekniktir. Kaputun altından tam olarak ne geçtiği önemli değil, program üzerindeki etkisinin önemi var.
ivan_pozdeev

150

Bu, işlevlere argüman aktarmanın bir yoludur. Referans yoluyla geçme, çağrılan fonksiyonların parametresi, arayanların geçirilen argümanı ile aynı olacaktır (değer değil, kimlik - değişkenin kendisi). Değere göre geçirme, çağrılan işlevlerin parametresi, arayanların geçirilen bağımsız değişkeninin bir kopyası olacağı anlamına gelir. Değer aynı olacaktır, ancak kimlik - değişken - farklıdır. Bu nedenle, çağrılan işlev tarafından yapılan bir parametrede yapılan değişiklikler bir durumda geçirilen argümanı değiştirir ve diğer durumda sadece çağrılan işlevdeki (yalnızca bir kopya olan) parametrenin değerini değiştirir. Acele edin:

  • Java yalnızca değere göre geçişi destekler. Bir nesneye bir referans kopyalarken, çağrılan işlevdeki parametre aynı nesneyi gösterecek ve bu nesnede yapılan değişiklikler arayanda görünecek olsa da, her zaman bağımsız değişkenleri kopyalar. Bu kafa karıştırıcı olabileceğinden, Jon Skeet'in bu konuda söyledikleri burada .
  • C #, değere göre geçişi ve referansla geçişi destekler (arayanda refkullanılan ve işlev olarak adlandırılan anahtar kelime ). Jon Skeet'in de burada güzel bir açıklaması var .
  • C ++, değere göre geçişi ve referansla geçişi destekler (çağrılan işlevde kullanılan referans parametre türü). Bunun bir açıklamasını aşağıda bulabilirsiniz.

Kodlar

Dilim C ++ olduğundan, burada kullanacağım

// 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'daki bir örnek zarar vermez:

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

Vikipedi

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

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

Bu adam hemen hemen çiviler:

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


9
neden iniş vekili? bir şey yanlışsa veya yanlış anlamalara yol açarsa lütfen yorum bırakın.
Johannes Schaub - litb

1
Benim oyum değildi, ama küçük bir noktada (biliyorsunuz, başkanlık tartışmalarında ele alınan tür) bunun bir "strateji" den daha "taktik" olduğunu söyleyebilirim.
harpo

28
Tamlık için +1. Downvotes hakkında endişelenmeyin - insanlar bunu garip nedenlerle yapıyor. Hesap makineleri ile ilgili bir görüş sorusunda, herkes programcıların hesap makinesi kullanması gerektiğini düşünmeyen bir adam tarafından reddedildi! Her neyse, cevabınızın çok iyi olduğunu düşündüm.
Mark Brittingham

1
Skeet'in açıklamalarına bağlantılar kesildi.
Para Odaklı Programcı

Skeet'in açıklamalarına bağlantılar hala kesik.
Rokit

85

Buradaki birçok cevap (ve özellikle en çok onaylanan cevap) aslında yanlıştır, çünkü "referans ile çağırma" nın gerçekten ne anlama geldiğini yanlış anlarlar. İşte sorunları düzeltmeye çalışıyorum.

TL; DR

En basit ifadeyle:

  • değere göre çağrı, değerleri işlev bağımsız değişkenleri olarak ilettiğiniz anlamına gelir
  • başvuru ile çağrı, değişkenleri işlev bağımsız değişkenleri olarak geçirdiğiniz anlamına gelir

Mecazi anlamda:

  • Değere göre çağrı , bir kağıda bir şeyler yazdığım ve size teslim ettiğim yerdir. Belki bir URL'dir, belki de Savaş ve Barış'ın tam bir kopyasıdır. Ne olursa olsun, size verdiğim bir kağıt parçası üzerinde ve şimdi etkili bir şekilde sizin kağıt parçanız . Artık o kağıda yazı yazabilir veya başka bir yerde bir şeyler bulmak ve onunla uğraşmak için bu kağıdı kullanabilirsiniz.
  • Referans olarak arama, içinde yazılı bir şey olan not defterimi verdiğim zamandır . Defterimde karalayabilirsin (belki de istiyorum, belki de istemiyorum) ve daha sonra defterimi oraya koyduğun karalamalar ile birlikte saklıyorum. Ayrıca, siz veya ben yazdıklarım başka bir yerde nasıl bir şey bulacağınız hakkında bilgi varsa, ya siz ya da ben oraya gidip bu bilgilerle uğraşabilirsiniz.

"Değere göre çağrı" ve "referansla çağrı" ne anlama gelmez

Bu kavramların her ikisinin de referans türleri kavramından tamamen bağımsız ve dikey olduğuna dikkat edin (Java'da alt türlerde Objectve C # tüm classtürlerinde tüm türlerdir) veya C'de (semantik olarak eşdeğer olan) işaretçi türleri kavramında Java'nın "referans türleri" ne, yalnızca farklı sözdizimiyle).

Referans türü kavramı bir URL'ye karşılık gelir: her ikisi de kendisi bir bilgi parçasıdır ve diğer bilgilere bir referanstır ( eğer isterseniz bir işaretçi ). Bir URL'nin farklı yerlerde çok sayıda kopyasına sahip olabilirsiniz ve hepsinin bağlantı verdikleri web sitesini değiştirmezler; web sitesi güncellenirse, her URL kopyası yine de güncellenmiş bilgilere yol açar. Tersine, URL'yi herhangi bir yerde değiştirmek, URL'nin yazılı başka bir kopyasını etkilemez.

Not C ++ "referanslar" (örneğin bir nosyonu vardır int&olduğunu) değil Java ve C # gibi 'ın 'referans türleri', ama bir 'referans ile çağrı' gibi. Java ve C # 'ın "referans türleri" ve Python'daki tüm türler, C ve C ++' nın "işaretçi türleri" (ör. int*) Olarak adlandırdığı gibidir.


Tamam, işte daha uzun ve daha resmi bir açıklama.

terminoloji

Öncelikle, bazı önemli terminoloji parçalarını vurgulamak, cevabımı açıklığa kavuşturmak ve kelimeleri kullanırken hepimizin aynı fikirlere atıfta bulunmasını sağlamak istiyorum. (Uygulamada, bu tür konularla ilgili kafa karışıklığının büyük çoğunluğunun, amaçlanan anlamı tam olarak iletmeyecek şekilde kelimeler kullanmaktan kaynaklandığına inanıyorum.)

Başlamak için, bir işlev bildiriminin bazı C benzeri dillerinde bir örnek:

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

İşte bu işlevi çağırmanın bir örneği:

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

Bu örneği kullanarak, bazı önemli terminoloji parçalarını tanımlamak istiyorum:

  • foosatır 1'de bildirilen bir işlevdir (Java, tüm işlev yöntemlerini yapmakta ısrar eder, ancak genelleme kaybı olmadan kavram aynıdır; C ve C ++, buraya girmeyeceğim bildirim ve tanım arasında bir ayrım yapar)
  • paramBir edilmektedir biçimsel parametre için foo, ayrıca 1. satırda beyan
  • arg2. satırda bildirilen ve başlatılan bir değişken , özellikle işlevin yerel bir değişkenibar
  • argbir de bağımsız değişken belirli için çağırma ve foohat 3

Burada ayırt edilecek iki önemli kavram kümesi vardır. Birincisi, değişkene karşı değerdir :

  • Bir değer olan bir ifade değerlendirme sonucu dilinde. Örneğin, baryukarıda belirtilen fonksiyon, satır sonra int arg = 1;, ekspresyon argvardır değeri 1 .
  • Bir değişken a, değerleri için konteyner . Bir değişken değiştirilebilir (bu çoğu C benzeri dilde varsayılan değerdir), salt okunur (örneğin Java finalveya C # 'ları kullanılarak bildirilir readonly) veya derinden değişmez (örn. C ++' lar kullanılarak const) olabilir.

Diğer önemli kavram çifti, parametreye karşı argümandır :

  • Bir parametre ( resmi parametre olarak da adlandırılır ) bir değişkendir bir işlevi çağırırken arayan tarafından sağlanması gereken .
  • Bağımsız değişken , bir işlevin arayan tarafından bu işlevin belirli bir resmi parametresini karşılamak için sağlanan bir değerdir

Değere göre çağrı

Değere göre çağrıda , işlevin resmi parametreleri, işlev çağırma için yeni oluşturulan ve bağımsız değişkenlerinin değerleri ile başlatılan değişkenlerdir.

Bu, diğer değişkenlerin değerlerle başlatıldığı gibi çalışır. Örneğin:

int arg = 1;
int another_variable = arg;

Burada argve another_variabletamamen bağımsız değişkenlerdir - değerleri birbirinden bağımsız olarak değişebilir. Bununla birlikte, another_variablebeyan edildiği noktada , aynı değeri tutmaya başlar arg- ki bu1 .

Bağımsız değişkenler olduklarından, yapılacak değişiklikler şunları another_variableetkilemez arg:

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

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

Bu tam arasındaki ilişki ile aynıdır argve paramben simetri için buraya tekrarlayacağız Yukarıdaki örneğimizde,:

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

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

Tam olarak kodu bu şekilde yazmıştık:

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

Yani, değere göre çağrının ne anlama geldiğinin tanımlayıcı özelliği , callee'nin ( foobu durumda) değerleri bağımsız değişken olarak alması , ancak arayanın değişkenlerinden bu değerler için kendi ayrı değişkenlerine sahip olmasıdır (bar bu durumda) .

Yukarıdaki metaforuma geri dönersem, eğer ben barve sen foo, seni aradığımda, üzerinde yazılı bir değere sahip bir kağıt parçası veririm . Sen buna kağıt parçası diyorsun param. Bu değer, defterimde (yerel değişkenlerim), aradığım bir değişkende yazdığım değerin bir kopyasıarg .

(Bir yana: donanım ve işletim sistemine bağlı olarak, bir işlevi diğerinden nasıl çağırdığınızla ilgili çeşitli çağrı kuralları vardır . Çağrı kuralı, değeri kağıdımın bir parçasına yazıp yazmayacağımı kararlaştırmak gibidir. ya da üzerine yazdığım bir kağıdınız varsa ya da ikimizin önündeki duvara yazarsam. Bu da ilginç bir konudur, ancak bu uzun cevabın kapsamı dışındadır.)

Referans ile arayın

Gelen referans olarak çağrı , işlevin biçimsel parametreler sade yeni isimler aynı değişkenlerin argümanları olarak o arayan malzemeleri için.

Yukarıdaki örneğimize geri dönersek, şuna eşittir:

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

Çünkü paramsadece başka bir isim arg- yani aynı değişkendir , değişiklikler paramyansıtılır arg. Bu, referansla yapılan çağrının, değere göre çağrıdan farklı olmasının temel yoludur.

Çok az sayıda dil referans ile çağrıyı destekler, ancak C ++ bunu şu şekilde yapabilir:

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

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

Bu durumda, gerçekte olduğu gibi paramaynı değere sahip değildir , (sadece farklı bir adla) ve artmış olduğunu gözlemleyebilir .arg argbararg

Not Bu olduğunu değil nasıl Java, JavaScript, C, Objective-C, Python ya da neredeyse herhangi bir başka popüler dil bugün eserlerin herhangi. Bu diller olan bu araçlar değil referans olarak diyoruz, onlar değere göre çağrı vardır.

Zeyilname: nesne paylaşımına göre çağrı

Sahip olduğunuz değer değere göre çağrı ise , ancak gerçek değer bir başvuru türü veya işaretçi türüyse , "değer" in kendisi çok ilginç değildir (örneğin, C'de yalnızca platforma özgü bir boyutun tamsayısıdır) - ne ilginç olan bu değerin ne olduğudur .

Bu referans türünün (yani, işaretçinin) işaret ettiği şey değiştirilebilirse , ilginç bir etki mümkündür: sivri uç değerini değiştirebilir ve arayan, çağıramazsa da sivri uç değerinde değişiklikler gözlemleyebilir işaretçinin kendisine değişir.

URL'nin benzetmesini tekrar ödünç almak için, URL'nin bir kopyasını size bir web sitesine verdim , ikimizin de önem verdiği şey URL değil, web sitesi ise özellikle ilginç değildir. URL kopyanızı karalamak sizin URL kopyamı etkilememesi sizin için önemli bir şey değildir (ve aslında Java ve Python gibi dillerde "URL" veya referans türü değeri, hiç değiştirilemez, sadece işaret ettiği şey olabilir).

Barbara Liskov, (bu semantiğe sahip olan) CLU programlama dilini icat ettiğinde, mevcut "değere göre çağrı" ve "referans olarak çağrı" terimlerinin bu yeni dilin anlambilimini tanımlamak için özellikle yararlı olmadığını fark etti. Böylece yeni bir terim icat etti: nesne paylaşımıyla çağrı .

Teknik olarak değere göre çağrılan, ancak kullanımda yaygın olan türlerin referans veya işaretçi türleri olduğu (yani: hemen hemen her modern zorunluluk, nesne yönelimli veya çok paradigma programlama dili) olduğu dilleri tartışırken, bunun daha az kafa karıştırıcı olduğunu düşünüyorum sadece değere göre arama veya referans olarak arama hakkında konuşmaktan kaçının . Nesne paylaşımıyla çağrı yapmak için sopa (veya sadece nesne ile çağrı ) ve kimse karışmaz. :-)


Daha İyi Açıklama: Burada ayırt etmek için iki önemli kavram kümesi vardır. The first is value versus variable. The other important pair of concepts to distinguish is parameter versus argument:
SK Venkat

2
Mükemmel cevap. Referans olarak yeni bir depolama biriminin oluşturulması gerekmediğini düşünüyorum. Parametre adı orijinal depolamaya (bellek)
başvurur

1
En iyi cevap IMO
Rafael Eyng

59

2 terimi anlamadan önce aşağıdakileri anlamanız GEREKİR . Her nesnenin ayırt edilebilecek 2 şeyi vardır.

  • Değeri.
  • Adresi.

Yani diyorsan employee.name = "John"

hakkında 2 şey olduğunu biliyorum name. Değeridir, bu değer "John"ve ayrıca belki bu gibi bazı onaltılık sayıdır bellekte konumu: 0x7fd5d258dd00.

Dilin mimarisine veya nesnenizin türüne (sınıf, yapı vb.) Bağlı olarak, aktarma "John"veya0x7fd5d258dd00

Geçme "John", değere göre geçme olarak bilinir. Geçme 0x7fd5d258dd00, referans olarak geçme olarak bilinir. Bu bellek konumuna işaret eden herkes, değerine erişebilir "John".

Bu konuda daha fazla bilgi için, bir işaretçinin kaydının silinmesi ve neden sınıf (yapı türü) üzerinde yapı (değer türü) seçildiğini okumanızı öneririz.


3
Ben arıyordum, aslında sadece açıklama değil, başparmak yukarıya kardeş kavramı bulmak gerekir.
Haisum Usman

Java her zaman değere göre geçer. Java'daki nesnelere referans vermek, değere göre geçiş olarak kabul edilir. Bu, "0x7fd5d258dd00 kodunun geçirilmesi, referans olarak iletilmesi olarak bilinir" ifadesiyle çelişir.
chetan raina

53

İşte bir örnek:

#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
Son satır 2 yerine 0 yazdırması gerektiği gibi bir sorun olduğunu düşünüyorum.
Taimoor Changaiz

@TaimoorChangaiz; Hangi "son satır"? Bu arada, IRC'yi kullanabiliyorsanız, lütfen Freenode üzerinde ## programlamaya gelin. Oradaki şeyleri açıklamak çok daha kolay olurdu. Nickim "pyon" var.
pyon

1
@ EduardoLeón by_val (y); std :: cout << y << std :: endl; // 2
Taimoor Changaiz

5
@TaimoorChangaiz: Neden 2 yazdırmıyor? yönceki satır tarafından 2'ye ayarlanmış. Neden 0'a geri dönecek?
pyon

@ EduardoLeón benim kötü. Evet haklısın. Düzeltme için teşekkürler
Taimoor Changaiz

28

Bunu almanın en basit yolu bir Excel dosyasındadır. Diyelim ki A1 ve B1 hücrelerinde 5 ve 2 olmak üzere iki sayınız var ve toplamlarını üçüncü bir hücrede bulmak istiyorsunuz, diyelim ki A2. Bunu iki şekilde yapabilirsiniz.

  • Ya bu hücreye = 5 + 2 yazarak değerlerini A2 hücresine ileterek . Bu durumda, A1 veya B1 hücrelerinin değerleri değişirse, A2'deki toplam aynı kalır.

  • Veya = A1 + B1 yazarak A1 ve B1 hücrelerinin "referanslarını" A2 hücresine aktararak . Bu durumda, A1 veya B1 hücrelerinin değerleri değişirse, A2'deki toplam da değişir.


Bu, diğer tüm cevaplar arasında en basit ve en iyi örnektir.
Amit Ray

18

Ref ile geçerken temelde değişkene bir işaretçi geçirirsiniz. Değere göre geç, değişkenin bir kopyasını geçiriyorsunuz. Temel kullanımda bu normalde çağıran yöntem olarak görünecek olan değişkene yapılan ref değişimleri ile geçiş ve alıştıkları değere göre geçiş anlamına gelir.


12

Değere göre iletme belirttiğiniz değişkende saklanan verilerin bir KOPYASINI gönderir, referansla iletme değişkenin kendisine doğrudan bir bağlantı gönderir. Bu nedenle, bir değişkeni başvuru ile iletir ve sonra geçirdiğiniz bloğun içindeki değişkeni değiştirirseniz, orijinal değişken değiştirilir. Yalnızca değere göre iletirseniz, orijinal değişken, ilettiğiniz blok tarafından değiştirilemez, ancak arama sırasında içerdiği her şeyin bir kopyasını alırsınız.


7

Değere göre ilet - İşlev değişkeni kopyalar ve bir kopyayla çalışır (böylece orijinal değişkendeki hiçbir şeyi değiştirmez)

Referansla ilet - İşlev orijinal değişkeni kullanır, diğer işlevdeki değişkeni değiştirirseniz, orijinal değişkende de değişir.

Örnek (bunu kendiniz kopyalayın ve kullanın / deneyin ve görün):

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

Basit tut, dikizlemek. Metin duvarları kötü bir alışkanlık olabilir.


Bu, parametre değerinin değiştirilip değiştirilmediğinin anlaşılmasında gerçekten yararlıdır, teşekkürler!
Kevin Zhao

5

Aralarındaki en büyük fark, değer tipi değişkenlerin değerleri depolamasıdır, bu nedenle bir yöntem çağrısında bir değer tipi değişken belirtmek, bu değişkenin değerinin bir kopyasını yönteme iletir. Başvuru türü değişkenleri nesnelere başvuruları depolar, bu nedenle bağımsız değişken olarak başvuru türü değişkeni belirtmek, yönteme nesneye başvuran gerçek başvurunun bir kopyasını iletir. Başvurunun kendisi değere göre iletilse de, yöntem yine de aldığı başvuruyu orijinal nesneyle etkileşimde bulunmak ve muhtemelen değiştirmek için kullanabilir. Benzer şekilde, bir yöntemden bir return ifadesi aracılığıyla bilgi döndürürken, yöntem, bir değer türü değişkeninde depolanan değerin bir kopyasını veya bir başvuru türü değişkeninde depolanan başvurunun bir kopyasını döndürür. Bir başvuru döndürüldüğünde, çağıran yöntem başvurulan nesneyle etkileşimde bulunmak için bu başvuruyu kullanabilir. Yani,

C # 'da, çağrılan yöntemin değişkeni değiştirebilmesi için bir değişkeni başvuru yoluyla iletmek üzere C #, ref ve out anahtar sözcüklerini sağlar. Ref anahtar sözcüğünü bir parametre bildirimine uygulamak, bir değişkeni yönteme referans olarak geçirmenizi sağlar; çağrılan yöntem, arayandaki orijinal değişkeni değiştirebilir. Ref anahtar sözcüğü, çağıran yöntemde zaten başlatılmış olan değişkenler için kullanılır. Normalde, bir yöntem çağrısı bağımsız değişken olarak başlatılmamış bir değişken içeriyorsa, derleyici bir hata oluşturur. Out parametresi ile bir parametrenin önüne bir çıkış parametresi oluşturur. Bu, derleyiciye argümanın çağrılan yönteme başvuru ile aktarılacağını ve çağrılan yöntemin arayandaki orijinal değişkene bir değer atayacağını belirtir. Yöntem, olası her yürütme yolunda çıkış parametresine bir değer atamazsa, derleyici bir hata oluşturur. Bu, derleyicinin bir yönteme bağımsız değişken olarak iletilen başlatılmamış bir değişken için hata iletisi oluşturmasını da önler. Bir yöntem, bir return deyimi aracılığıyla arayanına yalnızca bir değer döndürebilir, ancak birden çok çıkış (ref ve / veya out) parametresi belirterek birçok değer döndürebilir.

bkz. c # tartışma ve örnekler burada bağlantı metni


3

Örnekler:

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

const &genellikle en iyisidir. İnşaat ve yıkım cezasına çarptırılmazsınız. Referans sabit değilse, arayüzünüz iletilen verileri değiştireceğini önerir.


2

Kısacası, değere göre aktarılan değer NEDİR ve referansla aktarılan nerededir.

Değeriniz VAR1 = "Mutlu Adam!" İse, sadece "Mutlu Adam!" VAR1 "Mutlu Gal!" Olarak değişirse, bunu bilemezsiniz. Referans olarak geçtiyse ve VAR1 değişirse, yapacaksınız.


2

Orijinal değişkenin değerini bir işleve ilettikten sonra değiştirmek istemiyorsanız, işlev " by by value " ile oluşturulmalıdır. " parametresiyle oluşturulmalıdır.

Daha sonra işlev SADECE değere sahip olur, ancak iletilen değişkenin adresi olmaz. Değişkenin adresi olmadan, işlevin içindeki kod, işlevin dışından görüldüğü gibi değişken değerini değiştiremez.

Ancak, işleve, değişkenin değerini dışarıdan görüldüğü gibi değiştirme yeteneği vermek istiyorsanız , referansla pass kullanmanız gerekir . Hem değer hem de adres (başvuru) iletilir ve işlevin içinde bulunur.


1

değere göre geç, bağımsız değişkenleri kullanarak değerin bir işleve nasıl iletileceği anlamına gelir. değere göre geçişte, belirttiğimiz değişkende depolanan verileri kopyalarız ve veriler kopyalanarak referans bcse ile geçişten daha yavaştır. kopyalanan verilerde orijinal verilerden etkilenmeyen değişiklikler yaparız. refernce veya pass by address ile doğrudan değişkene doğrudan bağlantı göndeririz. veya işaretçiyi bir değişkene geçirme. daha hızlıdır, daha az zaman harcanır


0

Aşağıda, by by value - pointer değeri - reference arasındaki farkları gösteren bir örnek verilmiştir :

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
}

“Referans yoluyla geçme” yönteminin önemli bir sınırlaması vardır . Bir parametre başvuru ile iletildi olarak bildirilirse (öncesinde & işaretinden önce gelir) karşılık gelen gerçek parametresi bir değişken olmalıdır .

“Değere göre geçti” biçimindeki parametreye atıfta bulunan gerçek bir parametre genel olarak bir ifade olabilir , bu nedenle sadece bir değişkeni değil, aynı zamanda bir değişmezi, hatta bir işlev çağırma sonucunu da kullanmasına izin verilir.

İşlev, değişken dışında bir şeye değer koyamaz. Bir değişmeze yeni bir değer atayamaz veya bir ifadeyi sonucunu değiştirmeye zorlayamaz.

Not: Dylan Beattie'nin cevabını mevcut mesajda düz kelimelerle açıklayan olarak da kontrol edebilirsiniz.


"Bir parametre [referans olarak] olarak bildirilmişse, karşılık gelen gerçek parametresinin bir değişken olması gerektiğini" belirtirsiniz, ancak bu genel olarak doğru değildir. Bir başvuru geçici bir işleve (bir işlevin dönüş değeri gibi) bağlıysa, ömrü referansla eşleşecek şekilde uzatılır. Ayrıntılar için buraya bakın.
Chris Hunt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.