Saya memiliki kode berikut:
public class Tests {
public static void main(String[] args) throws Exception {
int x = 0;
while(x<3) {
x = x++;
System.out.println(x);
}
}
}
Kita tahu dia seharusnya menulis hanya x++
atau x=x+1
, tetapi pada x = x++
itu pertama-tama harus dikaitkan x
dengan dirinya sendiri, dan kemudian menambahkannya. Mengapa x
terus 0
sebagai nilai?
--memperbarui
Inilah bytecode:
public class Tests extends java.lang.Object{
public Tests();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_3
4: if_icmpge 22
7: iload_1
8: iinc 1, 1
11: istore_1
12: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
15: iload_1
16: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
19: goto 2
22: return
}
Saya akan membaca tentang petunjuk untuk mencoba memahami ...
x++
adalah pasca kenaikan;x=
adalah penugasan hasil ; yang hasil darix++
adalah aslix
(dan ada efek samping dari kenaikan, tapi itu tidak mengubah hasil), Jadi ini dapat diartikan sebagaivar tmp = x; x++; x = tmp;
Jawaban:
Catatan : Awalnya saya memposting kode C # dalam jawaban ini untuk tujuan ilustrasi, karena C # memungkinkan Anda untuk melewati
int
parameter dengan referensi denganref
kata kunci. Saya telah memutuskan untuk memperbaruinya dengan kode Java legal yang sebenarnya menggunakanMutableInt
kelas pertama yang saya temukan di Google untuk semacam perkiraan apa yangref
ada di C #. Saya benar-benar tidak tahu apakah itu membantu atau menyakiti jawabannya. Saya akan mengatakan bahwa saya secara pribadi belum melakukan semua pengembangan Java; jadi untuk semua yang saya tahu ada banyak cara idiomatis untuk menggambarkan hal ini.Mungkin jika kita menulis metode untuk melakukan hal yang setara dengan apa
x++
yang akan membuatnya lebih jelas.Baik? Tambahkan nilai yang diteruskan dan kembalikan nilai asli: itulah definisi dari operator peningkatan.
Sekarang, mari kita lihat bagaimana perilaku ini diputar dalam kode contoh Anda:
postIncrement(x)
melakukan apa? Penambahanx
, ya. Dan kemudian mengembalikan apa yangx
ada sebelum kenaikan . Nilai pengembalian ini kemudian ditugaskan untukx
.Jadi urutan nilai yang ditetapkan
x
adalah 0, lalu 1, lalu 0.Ini mungkin masih lebih jelas jika kita menulis ulang di atas:
Fiksasi Anda pada fakta bahwa ketika Anda mengganti
x
di sisi kiri dari tugas di atas dengany
, "Anda dapat melihat bahwa itu pertama kali menambah x, dan kemudian mengaitkannya dengan y" menurut saya bingung. Bukanx
yang ditugaskany
; ini adalah nilai yang sebelumnya ditugaskanx
. Sungguh, menyuntikkany
membuat segalanya tidak berbeda dari skenario di atas; kami hanya punya:Jadi sudah jelas:
x = x++
secara efektif tidak mengubah nilai x. Itu selalu menyebabkan x memiliki nilai x 0 , lalu x 0 +1, dan kemudian x 0 lagi.Pembaruan : Secara kebetulan, jangan sampai Anda ragu
x
akan pernah mendapat 1 "antara" operasi kenaikan dan penugasan dalam contoh di atas, saya telah mengumpulkan demo singkat untuk menggambarkan bahwa nilai perantara ini memang "ada," meskipun akan tidak pernah "terlihat" di utas pelaksana.Demo memanggil
x = x++;
secara berulang sementara utas terpisah terus mencetak nilaix
ke konsol.Di bawah ini adalah kutipan dari output program di atas. Perhatikan kejadian 1s dan 0s yang tidak beraturan.
sumber
Integer
kelas, yang merupakan bagian dari pustaka standar, dan bahkan memiliki manfaat menjadi kotak otomatis ke dan dariint
hampir secara transparan.x
dalam contoh terakhir Anda harus dinyatakanvolatile
, kalau tidak itu adalah perilaku yang tidak terdefinisi dan melihat1
s adalah implementasi spesifik.++
bisa dilakukan sebelum atau setelah penugasan. Secara praktis, mungkin ada kompiler yang melakukan hal yang sama dengan Java, tetapi Anda tidak ingin bertaruh.x = x++
bekerja dengan cara berikut:x++
. Evaluasi ekspresi ini menghasilkan nilai ekspresi (yang merupakan nilaix
sebelum kenaikan) dan kenaikanx
.x
, menimpa nilai tambah.Jadi, urutan kejadiannya tampak seperti berikut (ini adalah bytecode yang didekompilasi aktual, seperti yang dihasilkan oleh
javap -c
, dengan komentar saya):Sebagai perbandingan,
x = ++x
:sumber
iinc
menambahkan variabel, itu tidak menambah nilai stack, juga tidak meninggalkan nilai pada stack (tidak seperti hampir semua op aritmatika lainnya). Anda mungkin ingin menambahkan kode yang dihasilkan oleh++x
untuk perbandingan.Ini terjadi karena nilai
x
tidak bertambah sama sekali.setara dengan
Penjelasan:
Mari kita lihat kode byte untuk operasi ini. Pertimbangkan kelas sampel:
Sekarang menjalankan disassembler kelas pada ini kita dapatkan:
Sekarang Java VM berbasis stack yang berarti untuk setiap operasi, data akan didorong ke stack dan dari stack, data akan muncul untuk melakukan operasi. Ada juga struktur data lain, biasanya array untuk menyimpan variabel lokal. Variabel lokal diberikan id yang hanya indeks ke array.
Mari kita lihat mnemonik dalam
main()
metode:iconst_0
: Nilai konstan0
didorong ke stack.istore_1
: Elemen teratas stack muncul dan disimpan dalam variabel lokal dengan indeks1
yang
x
.iload_1
: Nilai di lokasi1
yang merupakan nilaix
tersebut0
, didorong ke dalam tumpukan.iinc 1, 1
: Nilai di lokasi memori1
bertambah dengan1
. Jadix
sekarang menjadi1
.istore_1
: Nilai di bagian atas tumpukan disimpan ke lokasi memori1
. Itu0
ditugaskan untukx
menimpa nilainya yang bertambah.Oleh karena itu nilai
x
tidak berubah yang mengakibatkan infinite loop.sumber
++
), tetapi variabel akan ditimpa kemudian.int temp = x; x = x + 1; x = temp;
lebih baik tidak menggunakan tautologi dalam contoh Anda.Namun "
=
" memiliki prioritas operator lebih rendah daripada "++
".Jadi
x=x++;
sebaiknya evaluasi sebagai berikutx
siap untuk penugasan (dievaluasi)x
bertambahx
ditugaskan kex
.sumber
++
memiliki prioritas lebih tinggi daripada=
di C dan C ++, tetapi pernyataan tersebut tidak didefinisikan dalam bahasa-bahasa tersebut.Tidak ada jawaban yang cukup tepat, jadi begini:
Saat Anda menulis
int x = x++
, Anda tidak menetapkanx
untuk menjadi dirinya sendiri pada nilai baru, Anda ditugaskanx
untuk menjadi nilai balikx++
ekspresi. Yang kebetulan merupakan nilai aslix
, seperti yang ditunjukkan dalam jawaban Colin Cochrane .Untuk bersenang-senang, uji kode berikut:
Hasilnya akan
Nilai balik dari ekspresi adalah nilai awal
x
, yaitu nol. Tetapi kemudian, ketika membaca nilaix
, kami menerima nilai yang diperbarui, yaitu satu.sumber
Sudah dijelaskan dengan baik oleh yang lain. Saya hanya menyertakan tautan ke bagian spesifikasi Java yang relevan.
x = x ++ adalah ekspresi. Java akan mengikuti urutan evaluasi . Pertama akan mengevaluasi ekspresi x ++, yang akan menambah x dan menetapkan nilai hasil ke nilai x sebelumnya . Kemudian akan menetapkan hasil ekspresi ke variabel x. Pada akhirnya, x kembali pada nilai sebelumnya.
sumber
Pernyataan ini:
mengevaluasi seperti ini:
x
ke tumpukan;x
;x
dari tumpukan.Jadi nilainya tidak berubah. Bandingkan dengan:
yang dievaluasi sebagai:
x
;x
ke tumpukan;x
dari tumpukan.Yang Anda inginkan adalah:
sumber
x++
potongan kode.x++;
solusix=x; x++;
Anda dan Anda melakukan apa yang Anda klaim sedang dilakukan oleh kode asli.Jawabannya sangat mudah. Ini ada hubungannya dengan urutan hal-hal yang dievaluasi.
x++
mengembalikan nilaix
lalu menambahkanx
.Konsekuensinya, nilai ekspresi
x++
adalah0
. Jadi, Anda menetapkanx=0
setiap kali dalam lingkaran. Tentu sajax++
menambah nilai ini, tetapi itu terjadi sebelum penugasan.sumber
Dari http://download.oracle.com/javase/tutorial/java/nutsandbolts/op1.html
Untuk menggambarkan, coba yang berikut ini:
Yang akan mencetak 1 dan 0.
sumber
Anda secara efektif mendapatkan perilaku berikut.
Gagasannya adalah bahwa operator pasca kenaikan (x ++) menambah variabel yang dipertanyakan SETELAH mengembalikan nilainya untuk digunakan dalam persamaan yang digunakan.
Sunting: Menambahkan sedikit karena komentar. Anggap saja seperti berikut.
sumber
Anda tidak benar-benar membutuhkan kode mesin untuk memahami apa yang terjadi.
Menurut definisi:
Operator penugasan mengevaluasi ekspresi sisi kanan, dan menyimpannya dalam variabel sementara.
1.1. Nilai x saat ini disalin ke variabel sementara ini
1.2. x bertambah sekarang.
Variabel sementara kemudian disalin ke sisi kiri ekspresi, yang kebetulan x! Jadi itu sebabnya nilai lama x lagi disalin ke dalam dirinya sendiri.
Ini sangat sederhana.
sumber
Ini karena tidak pernah bertambah dalam kasus ini.
x++
akan menggunakan nilai itu terlebih dahulu sebelum bertambah seperti pada kasus ini akan seperti:Tetapi jika Anda melakukan
++x;
ini akan meningkat.sumber
Nilai tetap pada 0 karena nilainya
x++
adalah 0. Dalam hal ini tidak masalah jika nilaix
meningkat atau tidak, tugasx=0
dijalankan. Ini akan menimpa nilai kenaikan sementarax
(yaitu 1 untuk "waktu yang sangat singkat").sumber
x++
, bukan untuk seluruh penugasanx=x++;
Ini berfungsi seperti yang Anda harapkan. Perbedaan antara awalan dan postfix.
sumber
Pikirkan x ++ sebagai pemanggilan fungsi yang "mengembalikan" apa X sebelum kenaikan (itulah mengapa ini disebut kenaikan pasca).
Jadi urutan operasi adalah:
1: cache nilai x sebelum incrementing
2: increment x
3: kembalikan nilai yang di-cache (x sebelum di-incremented)
4: nilai return ditugaskan ke x
sumber
Ketika ++ berada di rhs, hasilnya dikembalikan sebelum angkanya bertambah. Ubah ke ++ x dan itu akan baik-baik saja. Java akan mengoptimalkan ini untuk melakukan operasi tunggal (penugasan x ke x) daripada kenaikan.
sumber
Sejauh yang saya bisa lihat, kesalahan itu terjadi, karena penugasan mengesampingkan nilai yang bertambah, dengan nilai sebelum kenaikan, yaitu ia membatalkan kenaikan.
Secara khusus, ekspresi "x ++", memiliki nilai 'x' sebelum kenaikan dibandingkan dengan "++ x" yang memiliki nilai 'x' setelah penambahan.
Jika Anda tertarik untuk menyelidiki bytecode, kami akan melihat tiga baris yang dimaksud:
7: iload_1 # Akan meletakkan nilai variabel lokal ke-2 di stack
8: iinc 1,1 # akan menambah variabel lokal ke-2 dengan 1, perhatikan bahwa ia membiarkan stack tidak tersentuh!
9: istore_1 # Akan memunculkan bagian atas tumpukan dan menyimpan nilai elemen ini ke variabel lokal ke-2
(Anda dapat membaca efek dari setiap instruksi JVM di sini )
Inilah sebabnya mengapa kode di atas akan berulang tanpa batas, sedangkan versi dengan ++ x tidak akan. Bytecode untuk ++ x akan terlihat sangat berbeda, sejauh yang saya ingat dari kompiler Java 1.3 yang saya tulis sedikit lebih dari setahun yang lalu, bytecode harus seperti ini:
Jadi, hanya menukar dua baris pertama, ubah semantik sehingga nilai tertinggal di atas tumpukan, setelah kenaikan (yaitu 'nilai' ekspresi) adalah nilai setelah kenaikan.
sumber
Begitu:
Sedangkan
Begitu:
Tentu saja hasil akhirnya adalah sama dengan hanya
x++;
atau++x;
pada baris dengan sendirinya.sumber
karena pernyataan di atas x tidak pernah mencapai 3;
sumber
Saya bertanya-tanya apakah ada sesuatu di spec Java yang secara tepat mendefinisikan perilaku ini. (Implikasi yang jelas dari pernyataan itu adalah bahwa saya terlalu malas untuk memeriksa.)
Catatan dari bytecode Tom, garis kuncinya adalah 7, 8 dan 11. Baris 7 memuat x ke dalam tumpukan perhitungan. Peningkatan baris 8 x. Baris 11 menyimpan nilai dari tumpukan kembali ke x. Dalam kasus normal di mana Anda tidak menetapkan nilai kembali ke diri mereka sendiri, saya tidak berpikir akan ada alasan mengapa Anda tidak dapat memuat, menyimpan, lalu menambah. Anda akan mendapatkan hasil yang sama.
Seperti, misalkan Anda memiliki kasus yang lebih normal di mana Anda menulis sesuatu seperti: z = (x ++) + (y ++);
Apakah itu dikatakan (pseudocode to skip technicalities)
atau
harus tidak relevan. Entah implementasi harus valid, saya akan berpikir.
Saya akan sangat berhati-hati dalam menulis kode yang tergantung pada perilaku ini. Ini terlihat sangat tergantung pada implementasi, antara-celah-dalam-spesifikasi bagi saya. Satu-satunya waktu yang akan membuat perbedaan adalah jika Anda melakukan sesuatu yang gila, seperti contoh di sini, atau jika Anda memiliki dua utas berjalan dan bergantung pada urutan evaluasi dalam ekspresi.
sumber
Saya pikir karena di Java ++ memiliki prioritas lebih tinggi daripada = (tugas) ... Apakah itu? Lihatlah http://www.cs.uwf.edu/~eelsheik/cop2253/resources/op_precedence.html ...
Cara yang sama jika Anda menulis x = x + 1 ... + memiliki prioritas lebih tinggi daripada = (tugas)
sumber
++
memiliki prioritas lebih tinggi daripada=
di C dan C ++ juga, tetapi pernyataan itu tidak terdefinisi.The
x++
ekspresi mengevaluasi kex
. Bagian itu++
mempengaruhi nilai setelah evaluasi , bukan setelah pernyataan . jadix = x++
secara efektif diterjemahkan ke dalamsumber
Sebelum menambah nilai satu, nilai ditugaskan ke variabel.
sumber
Itu terjadi karena posnya bertambah. Ini berarti bahwa variabel bertambah setelah ekspresi dievaluasi.
x sekarang 10, tetapi y adalah 9, nilai x sebelum dinaikkan.
Lihat lebih lanjut di Definisi Peningkatan Pasca .
sumber
x
/y
contoh berbeda dari kode nyata, dan perbedaan relevan. Tautan Anda bahkan tidak menyebutkan Java. Selama dua bahasa itu tidak menyebutkan, pernyataan dalam pertanyaan tidak terdefinisi.Periksa kode di bawah ini,
output akan menjadi,
post increment
berarti kenaikan nilai dan kembalikan nilai sebelum kenaikan . Itulah sebabnya nilaitemp
adalah0
. Jadi bagaimana jikatemp = i
dan ini dalam satu lingkaran (kecuali untuk baris kode pertama). seperti dalam pertanyaan !!!!sumber
Operator kenaikan diterapkan ke variabel yang sama seperti yang Anda tetapkan. Itu meminta masalah. Saya yakin Anda dapat melihat nilai variabel x Anda saat menjalankan program ini .... itu harus menjelaskan mengapa loop tidak pernah berakhir.
sumber