Apa perbedaan antara
- parameter yang dilewatkan oleh referensi
- sebuah parameter dilewatkan oleh nilai?
Bisakah Anda memberi saya beberapa contoh?
language-agnostic
pass-by-reference
pass-by-value
Deduplicator
sumber
sumber
Jawaban:
Pertama dan terutama, perbedaan "pass by value vs. pass by reference" seperti yang didefinisikan dalam teori CS sekarang sudah usang karena teknik yang awalnya didefinisikan sebagai "pass by reference" sejak itu tidak disukai dan jarang digunakan sekarang. 1
Bahasa yang lebih baru 2 cenderung menggunakan pasangan teknik yang berbeda (tetapi serupa) untuk mencapai efek yang sama (lihat di bawah) yang merupakan sumber utama kebingungan.
Sumber kebingungan kedua adalah fakta bahwa dalam "pass by reference", "reference" memiliki arti yang lebih sempit daripada istilah umum "reference" (karena frasa yang mendahului).
Sekarang, definisi otentik adalah:
Ketika suatu parameter dilewatkan dengan referensi , pemanggil dan callee menggunakan variabel yang sama untuk parameter. Jika callee memodifikasi variabel parameter, efeknya terlihat oleh variabel pemanggil.
Ketika suatu parameter dilewatkan oleh nilai , penelepon dan callee memiliki dua variabel independen dengan nilai yang sama. Jika callee memodifikasi variabel parameter, efeknya tidak terlihat oleh pemanggil.
Hal yang perlu diperhatikan dalam definisi ini adalah:
"Variabel" di sini berarti variabel penelepon (lokal atau global) itu sendiri - yaitu jika saya melewatkan variabel lokal dengan referensi dan menugaskannya, saya akan mengubah variabel penelepon itu sendiri, bukan misalnya apa pun yang menunjuk jika itu adalah penunjuk .
Arti "referensi" dalam "lewat referensi" . Perbedaannya dengan istilah "referensi" umum adalah bahwa "referensi" ini bersifat sementara dan implisit. Apa yang pada dasarnya didapatkan oleh callee adalah "variabel" yang entah bagaimana "sama" dengan yang asli. Seberapa spesifik efek ini dicapai adalah tidak relevan (mis. Bahasa juga dapat mengekspos beberapa detail implementasi - alamat, pointer, dereferencing - ini semua tidak relevan; jika efek bersihnya adalah ini, ini hanya sebagai referensi).
Sekarang, dalam bahasa modern, variabel cenderung berasal dari "tipe referensi" (konsep lain ditemukan lebih lambat dari "lewat referensi" dan diilhami olehnya), yaitu data objek aktual disimpan secara terpisah di suatu tempat (biasanya, di heap), dan hanya "referensi" yang pernah disimpan dalam variabel dan dikirimkan sebagai parameter. 3
Melewati referensi seperti itu jatuh di bawah nilai by-pass karena nilai variabel secara teknis referensi itu sendiri, bukan objek yang dirujuk. Namun, efek bersih pada program bisa sama dengan pass-by-value atau pass-by-reference:
Seperti yang Anda lihat, pasangan teknik ini hampir sama dengan yang ada di definisi, hanya dengan tingkat tipuan: ganti saja "variabel" dengan "objek yang direferensikan".
Tidak ada nama yang disepakati untuk mereka, yang mengarah ke penjelasan yang berkerut seperti "panggilan berdasarkan nilai di mana nilainya adalah referensi". Pada tahun 1975, Barbara Liskov menyarankan istilah " call-by-object-sharing " (atau kadang-kadang hanya "call-by-sharing") meskipun tidak pernah cukup populer. Selain itu, tidak satu pun dari frasa ini yang sejajar dengan pasangan aslinya. Tidak heran istilah lama akhirnya digunakan kembali tanpa adanya sesuatu yang lebih baik, yang menyebabkan kebingungan. 4
CATATAN : Untuk waktu yang lama, jawaban ini digunakan untuk mengatakan:
Ini sebagian besar benar kecuali arti yang lebih sempit dari "referensi" - itu bersifat sementara dan implisit (tidak harus, tetapi menjadi eksplisit dan / atau persisten adalah fitur tambahan, bukan bagian dari semantik pass-by-reference , seperti dijelaskan di atas). Analogi yang lebih dekat akan memberi Anda salinan dokumen vs mengundang Anda untuk mengerjakan yang asli.
1 Kecuali Anda memprogram dalam Fortran atau Visual Basic, itu bukan perilaku default, dan dalam sebagian besar bahasa dalam penggunaan modern, panggilan-dengan-referensi yang sebenarnya bahkan tidak mungkin.
2 Sejumlah yang lebih tua juga mendukungnya
3 Dalam beberapa bahasa modern, semua tipe adalah tipe referensi. Pendekatan ini dipelopori oleh bahasa CLU pada tahun 1975 dan sejak itu telah diadopsi oleh banyak bahasa lain, termasuk Python dan Ruby. Dan masih banyak lagi bahasa yang menggunakan pendekatan hybrid, di mana beberapa tipe adalah "tipe nilai" dan yang lain adalah "tipe referensi" - di antaranya adalah C #, Java, dan JavaScript.
4 Tidak ada yang buruk dengan mendaur ulang istilah lama yang pas , tapi kita harus membuatnya dengan jelas makna mana yang digunakan setiap kali. Tidak melakukan hal itulah yang menyebabkan kebingungan.
sumber
Ini adalah cara bagaimana menyampaikan argumen ke fungsi. Melewati dengan referensi berarti parameter fungsi yang dipanggil akan sama dengan argumen yang dilewati oleh penelepon (bukan nilai, tetapi identitas - variabel itu sendiri). Lewat nilai berarti parameter fungsi yang dipanggil akan menjadi salinan dari argumen yang dilewati penelepon. Nilainya akan sama, tetapi identitas - variabel - berbeda. Jadi perubahan pada parameter yang dilakukan oleh fungsi yang dipanggil dalam satu kasus mengubah argumen yang diteruskan dan dalam kasus lain hanya mengubah nilai parameter dalam fungsi yang disebut (yang hanya merupakan salinan). Terburu-buru cepat:
ref
digunakan pada fungsi pemanggil dan dipanggil). Jon Skeet juga memiliki penjelasan yang bagus tentang ini di sini .Kode
Karena bahasa saya adalah C ++, saya akan menggunakannya di sini
Dan contoh di Jawa tidak akan sakit:
Wikipedia
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value
http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference
Orang ini cukup berhasil:
http://javadude.com/articles/passbyvalue.htm
sumber
Banyak jawaban di sini (dan khususnya jawaban yang paling banyak dipilih) secara faktual salah, karena mereka salah mengerti apa arti "panggilan dengan referensi". Inilah upaya saya untuk meluruskan masalah.
TL; DR
Secara sederhana:
Dalam istilah metaforis:
Apa "panggilan berdasarkan nilai" dan "panggilan dengan referensi" tidak berarti
Perhatikan bahwa kedua konsep ini sepenuhnya independen dan ortogonal dari konsep tipe referensi (yang di Jawa adalah semua tipe yang merupakan subtipe dari
Object
, dan dalam semuaclass
tipe C ), atau konsep tipe pointer seperti dalam C (yang secara semantik setara untuk "tipe referensi" Java, hanya dengan sintaks yang berbeda).Gagasan tipe referensi sesuai dengan URL: itu sendiri merupakan sepotong informasi, dan merupakan referensi (a pointer , jika Anda mau) ke informasi lain. Anda dapat memiliki banyak salinan URL di tempat yang berbeda, dan mereka tidak mengubah situs web yang mereka tautkan; jika situs web diperbarui maka setiap salinan URL masih akan mengarah pada informasi yang diperbarui. Sebaliknya, mengubah URL di satu tempat tidak akan memengaruhi salinan tertulis URL lainnya.
Perhatikan bahwa C ++ memiliki gagasan "referensi" (misalnya
int&
) yang tidak seperti Java dan C # 's 'referensi jenis', tetapi adalah seperti 'panggilan dengan referensi'. "Tipe referensi" Java dan C #, dan semua tipe dalam Python, seperti apa yang C dan C ++ sebut "tipe pointer" (misint*
.).OKE, inilah penjelasan yang lebih panjang dan lebih formal.
Terminologi
Untuk mulai dengan, saya ingin menyoroti beberapa terminologi penting, untuk membantu memperjelas jawaban saya dan untuk memastikan kita semua mengacu pada ide yang sama ketika kita menggunakan kata-kata. (Dalam praktiknya, saya percaya sebagian besar kebingungan tentang topik-topik seperti ini berasal dari penggunaan kata-kata dengan cara yang tidak sepenuhnya mengomunikasikan makna yang dimaksudkan.)
Untuk memulai, berikut adalah contoh dalam beberapa bahasa mirip C dari deklarasi fungsi:
Dan inilah contoh memanggil fungsi ini:
Dengan menggunakan contoh ini, saya ingin mendefinisikan beberapa bit penting dari terminologi:
foo
adalah fungsi yang dideklarasikan pada baris 1 (Java bersikeras membuat semua metode fungsi, tetapi konsepnya sama tanpa kehilangan keumuman; C dan C ++ membuat perbedaan antara deklarasi dan definisi yang tidak akan saya bahas di sini)param
adalah parameter formal untukfoo
, juga dinyatakan pada baris 1arg
adalah variabel , khususnya variabel lokal dari fungsibar
, dideklarasikan dan diinisialisasi pada baris 2arg
juga merupakan argumen untuk spesifik doa darifoo
on line 3Ada dua rangkaian konsep yang sangat penting untuk dibedakan di sini. Yang pertama adalah nilai versus variabel :
bar
fungsi di atas, setelah barisint arg = 1;
, ekspresiarg
memiliki nilai1
.final
atau C # 'sreadonly
) atau sangat tidak dapat diubah (misalnya menggunakan C ++' sconst
).Sepasang konsep penting lainnya untuk dibedakan adalah parameter versus argumen :
Panggil berdasarkan nilai
Dalam panggilan dengan nilai , parameter formal fungsi adalah variabel yang baru dibuat untuk pemanggilan fungsi, dan yang diinisialisasi dengan nilai argumennya.
Ini bekerja persis dengan cara yang sama seperti jenis variabel lainnya diinisialisasi dengan nilai. Sebagai contoh:
Di sini
arg
dananother_variable
merupakan variabel yang sepenuhnya independen - nilainya dapat berubah secara independen satu sama lain. Namun, pada titik di manaanother_variable
dinyatakan, diinisialisasi untuk memegang nilai yang sama dengan yangarg
memegang - yaitu1
.Karena mereka adalah variabel independen, perubahan
another_variable
tidak mempengaruhiarg
:Ini persis sama dengan hubungan antara
arg
danparam
dalam contoh kita di atas, yang akan saya ulangi di sini untuk simetri:Persis seperti kita telah menulis kode dengan cara ini:
Yaitu, karakteristik yang menentukan dari apa yang disebut dengan nilai berarti bahwa callee (
foo
dalam hal ini) menerima nilai sebagai argumen, tetapi memiliki variabel tersendiri untuk nilai-nilai tersebut dari variabel pemanggil (bar
dalam kasus ini).Kembali ke metafora saya di atas, jika saya
bar
dan Andafoo
, ketika saya memanggil Anda, saya menyerahkan selembar kertas dengan nilai tertulis di atasnya. Anda menyebut selembar kertas ituparam
. Nilai itu adalah salinan dari nilai yang saya tulis di notebook saya (variabel lokal saya), dalam variabel yang saya panggilarg
.(Sebagai tambahan: tergantung pada perangkat keras dan sistem operasi, ada berbagai konvensi panggilan tentang bagaimana Anda memanggil satu fungsi dari yang lain. Konvensi panggilan seperti kita memutuskan apakah saya menulis nilai pada selembar kertas saya dan kemudian menyerahkannya kepada Anda , atau jika Anda memiliki selembar kertas yang saya tuliskan, atau jika saya menulisnya di dinding di depan kami berdua. Ini juga merupakan subjek yang menarik, tetapi jauh di luar cakupan jawaban yang sudah lama ini.)
Panggil dengan referensi
Dalam panggilan dengan referensi , parameter formal fungsi hanyalah nama baru untuk variabel yang sama dengan yang diberikan pemanggil sebagai argumen.
Kembali ke contoh kami di atas, itu setara dengan:
Karena
param
hanya nama lain untukarg
- yaitu, mereka adalah variabel yang sama , perubahanparam
tercermin dalamarg
. Ini adalah cara mendasar di mana panggilan dengan referensi berbeda dari panggilan dengan nilai.Sangat sedikit bahasa yang mendukung panggilan dengan referensi, tetapi C ++ dapat melakukannya seperti ini:
Dalam hal ini,
param
tidak hanya harus sama nilai sepertiarg
, itu benar-benar adalaharg
(hanya dengan nama yang berbeda) danbar
dapat mengamati bahwaarg
telah bertambah.Perhatikan bahwa ini bukan cara Java, JavaScript, C, Objective-C, Python, atau hampir semua bahasa populer lainnya saat ini. Ini berarti bahwa bahasa-bahasa itu bukan panggilan dengan referensi, mereka adalah panggilan dengan nilai.
Tambahan: panggilan dengan berbagi objek
Jika yang Anda miliki adalah panggilan dengan nilai , tetapi nilai sebenarnya adalah tipe referensi atau tipe pointer , maka "nilai" itu sendiri tidak terlalu menarik (misalnya dalam C itu hanya bilangan bulat dari ukuran platform-spesifik) - apa menarik adalah nilai apa yang ditunjukkan .
Jika apa yang ditunjuk oleh tipe referensi (yaitu, pointer) dapat diubah maka efek yang menarik mungkin terjadi: Anda dapat memodifikasi nilai menunjuk-ke, dan penelepon dapat mengamati perubahan pada nilai menunjuk-ke, meskipun penelepon tidak dapat mengamati perubahan pada pointer itu sendiri.
Untuk meminjam analogi URL lagi, fakta bahwa saya memberi Anda salinannya URL ke situs web tidak terlalu menarik jika hal yang kami berdua pedulikan adalah situs web, bukan URL. Fakta bahwa Anda menulis salinan URL tidak memengaruhi salinan URL saya bukanlah hal yang kami pedulikan (dan pada kenyataannya, dalam bahasa seperti Java dan Python "URL", atau nilai jenis referensi, dapat tidak dapat dimodifikasi sama sekali, hanya hal yang ditunjukkan olehnya yang dapat).
Barbara Liskov, ketika dia menemukan bahasa pemrograman CLU (yang memiliki semantik ini), menyadari bahwa istilah yang ada "panggilan berdasarkan nilai" dan "panggilan dengan referensi" tidak terlalu berguna untuk menggambarkan semantik bahasa baru ini. Jadi dia menemukan istilah baru: panggilan dengan berbagi objek .
Ketika membahas bahasa yang secara teknis disebut oleh nilai, tetapi di mana jenis yang umum digunakan adalah tipe referensi atau penunjuk (yaitu: hampir setiap bahasa pemrograman imperatif, berorientasi objek, atau multi-paradigma modern), saya merasa jauh lebih membingungkan untuk hanya menghindari berbicara tentang panggilan berdasarkan nilai atau panggilan dengan referensi . Stick untuk memanggil dengan berbagi objek (atau hanya memanggil dengan objek ) dan tidak ada yang akan bingung. :-)
sumber
The first is value versus variable.
The other important pair of concepts to distinguish is parameter versus argument:
Sebelum memahami 2 istilah, Anda HARUS memahami yang berikut. Setiap benda, memiliki 2 hal yang dapat membuatnya dibedakan.
Jadi, jika Anda katakan
employee.name = "John"
ketahuilah bahwa ada 2 hal tentang
name
. Nilainya yang"John"
dan juga lokasi di memori yang beberapa nomor heksadesimal mungkin seperti ini:0x7fd5d258dd00
.Bergantung pada arsitektur bahasa atau jenis (kelas, struct, dll.) Objek Anda, Anda dapat mentransfer
"John"
atau0x7fd5d258dd00
Passing
"John"
dikenal sebagai passing oleh nilai. Passing0x7fd5d258dd00
dikenal sebagai passing oleh referensi. Siapa pun yang menunjuk ke lokasi memori ini akan memiliki akses ke nilai"John"
.Untuk lebih lanjut tentang ini, saya sarankan Anda untuk membaca tentang dereferencing pointer dan juga mengapa memilih struct (tipe nilai) dari kelas (tipe referensi)
sumber
Berikut ini sebuah contoh:
sumber
y
telah diatur ke 2 oleh baris sebelumnya. Mengapa kembali ke 0?Cara paling sederhana untuk mendapatkan ini adalah pada file Excel. Katakanlah misalnya Anda memiliki dua angka, 5 dan 2 dalam sel A1 dan B1 yang sesuai, dan Anda ingin menemukan jumlah mereka dalam sel ketiga, katakanlah A2. Anda dapat melakukan ini dengan dua cara.
Baik dengan mengirimkan nilainya ke sel A2 dengan mengetik = 5 + 2 ke dalam sel ini. Dalam hal ini, jika nilai sel A1 atau B1 berubah, jumlah dalam A2 tetap sama.
Atau dengan melewatkan "referensi" dari sel A1 dan B1 ke sel A2 dengan mengetik = A1 + B1 . Dalam hal ini, jika nilai sel A1 atau B1 berubah, jumlah dalam A2 juga berubah.
sumber
Ketika melewati oleh ref Anda pada dasarnya melewatkan pointer ke variabel. Lewati nilai Anda melewati salinan variabel. Dalam penggunaan dasar ini biasanya berarti perubahan ref by to variabel akan terlihat sebagai metode pemanggilan dan nilai by pass yang tidak biasa.
sumber
Pass by value mengirimkan COPY dari data yang disimpan dalam variabel yang Anda tentukan, pass by reference mengirimkan tautan langsung ke variabel itu sendiri. Jadi, jika Anda melewatkan variabel dengan referensi dan kemudian mengubah variabel di dalam blok tempat Anda meneruskannya, variabel asli akan berubah. Jika Anda hanya memberikan nilai, variabel asli tidak akan dapat diubah oleh blok yang Anda berikan, tetapi Anda akan mendapatkan salinan apa pun yang ada pada saat panggilan berlangsung.
sumber
Pass by value - Fungsi menyalin variabel dan bekerja dengan salinan (sehingga tidak mengubah apa pun di variabel asli)
Lulus dengan referensi - Fungsi ini menggunakan variabel asli, jika Anda mengubah variabel di fungsi lain, itu berubah dalam variabel asli juga.
Contoh (salin dan gunakan / coba sendiri dan lihat):
Tetap sederhana, intip. Dinding teks bisa menjadi kebiasaan buruk.
sumber
Perbedaan utama di antara mereka adalah bahwa variabel tipe-nilai menyimpan nilai, jadi menentukan variabel tipe-nilai dalam pemanggilan metode meneruskan salinan nilai variabel itu ke metode. Variabel tipe referensi menyimpan referensi ke objek, jadi tentukan variabel tipe referensi sebagai argumen yang meneruskan metode salinan referensi aktual yang merujuk ke objek. Meskipun referensi itu sendiri dilewatkan oleh nilai, metode ini masih dapat menggunakan referensi yang diterimanya untuk berinteraksi dengan - dan mungkin memodifikasi - objek asli. Demikian pula, ketika mengembalikan informasi dari suatu metode melalui pernyataan kembali, metode mengembalikan salinan nilai yang disimpan dalam variabel tipe nilai atau salinan referensi yang disimpan dalam variabel tipe referensi. Ketika referensi dikembalikan, metode pemanggilan dapat menggunakan referensi itu untuk berinteraksi dengan objek yang direferensikan. Begitu,
Dalam c #, untuk mengirimkan variabel dengan referensi sehingga metode yang dipanggil dapat memodifikasi variabel, C # menyediakan kata kunci ref dan out. Menerapkan kata kunci ref ke deklarasi parameter memungkinkan Anda untuk mengirimkan variabel ke metode dengan referensi — metode yang dipanggil akan dapat memodifikasi variabel asli di pemanggil. Kata kunci ref digunakan untuk variabel yang sudah diinisialisasi dalam metode panggilan. Biasanya, ketika pemanggilan metode berisi variabel yang tidak diinisialisasi sebagai argumen, kompiler menghasilkan kesalahan. Sebelum parameter dengan kata kunci keluar menciptakan parameter output. Ini menunjukkan kepada kompiler bahwa argumen akan diteruskan ke metode yang dipanggil dengan referensi dan bahwa metode yang dipanggil akan memberikan nilai ke variabel asli di pemanggil. Jika metode ini tidak menetapkan nilai ke parameter output di setiap jalur eksekusi yang mungkin, kompiler menghasilkan kesalahan. Ini juga mencegah kompilator menghasilkan pesan kesalahan untuk variabel tidak diinisialisasi yang dilewatkan sebagai argumen ke metode. Suatu metode dapat mengembalikan hanya satu nilai ke pemanggilnya melalui pernyataan pengembalian, tetapi dapat mengembalikan banyak nilai dengan menetapkan beberapa parameter keluaran (ref dan / atau keluar).
lihat diskusi # c dan contoh di sini tautan teks
sumber
Contoh:
const &
umumnya yang terbaik. Anda tidak dikenakan hukuman konstruksi dan penghancuran. Jika referensi bukan const antarmuka Anda menyarankan bahwa itu akan mengubah data yang dikirimkan.sumber
Singkatnya, Lulus oleh nilai adalah APA itu dan disahkan oleh referensi MANA itu.
Jika nilai Anda adalah VAR1 = "Happy Guy!", Anda hanya akan melihat "Happy Guy!". Jika VAR1 berubah menjadi "Happy Gal!", Anda tidak akan tahu itu. Jika lulus dengan referensi, dan VAR1 berubah, Anda akan melakukannya.
sumber
Jika Anda tidak ingin mengubah nilai variabel asli setelah meneruskannya ke fungsi, fungsi tersebut harus dibangun dengan parameter " pass by value ".
Maka fungsi HANYA akan memiliki nilai tetapi bukan alamat variabel yang diteruskan. Tanpa alamat variabel, kode di dalam fungsi tidak dapat mengubah nilai variabel seperti yang terlihat dari luar fungsi.
Tetapi jika Anda ingin memberikan fungsi kemampuan untuk mengubah nilai variabel seperti yang terlihat dari luar, Anda perlu menggunakan referensi lewat . Karena nilai dan alamat (referensi) dilewatkan dan tersedia di dalam fungsi.
sumber
pass by value berarti cara meneruskan nilai ke suatu fungsi dengan memanfaatkan argumen. dalam pass by value kita menyalin data yang disimpan dalam variabel yang kita tentukan dan itu lebih lambat daripada pass by reference karena data disalin. dari kami membuat perubahan dalam data yang disalin data asli tidak terpengaruh. dan melalui pass by refernce atau pass by address kami mengirimkan tautan langsung ke variabel itu sendiri. atau melewatkan pointer ke suatu variabel. lebih cepat karena lebih sedikit waktu yang dikonsumsi
sumber
Berikut adalah contoh yang menunjukkan perbedaan antara nilai by pass - nilai pointer - referensi :
Metode “passing by reference” memiliki batasan penting . Jika suatu parameter dinyatakan sebagai lulus oleh referensi (sehingga didahului oleh tanda &) parameter aktualnya yang sesuai harus berupa variabel .
Parameter aktual yang merujuk pada parameter formal "lulus oleh nilai" dapat berupa ekspresi secara umum, sehingga diizinkan untuk menggunakan tidak hanya variabel tetapi juga hasil doa fungsi literal atau bahkan fungsi.
Fungsi tidak dapat menempatkan nilai pada sesuatu selain variabel. Itu tidak bisa menetapkan nilai baru ke literal atau memaksa ekspresi untuk mengubah hasilnya.
PS: Anda juga dapat memeriksa jawaban Dylan Beattie di utas saat ini yang menjelaskannya dengan kata-kata sederhana.
sumber