Java Tuzakları – IV: Java bizimle dalga mı geçiyor?
Birisi size gelip, aşağıdaki kodun “0” bastığını söylese, gözlerinizi ovalayıp koda bir kaç defa daha bakmaktan ve inanmayıp, gidip aynısını yazıp çalıştırmaktan başka ne yapardınız?
... int i = 0; i = i++; System.out.println(i); ...
Aynı kodu aşağıdaki gibi yazarsanız, beklediğiniz olur ve “0” yerine “1” basılır. Siz de derin bir ohh çekersiniz 🙂
... int i = 0; int j = i++; System.out.println(i); ...
Ama kodda “j” yerine “i” koyarsanız, kabus yine başlar.
Aslında problem Java’da değil. Eminim bu kodu aynı şekilde çalıştıran başka diller ya da derleyiciler de vardır. C/C++’ta bu durumun derleyiciye bağlı olduğunu biliyorum örneğin.
Ben bu kodu görünce, hemen “javap”ı kullanarak üretilen bytecodea baktım ve ne olup bittiğini anlamaya çalıştım. “javac”ın ürettiği bytecode şöyle:
Code: 0: iconst_0 1: istore_0 2: iload_0 3: iinc 0, 1 6: istore_0
Malum JVM bir stack makinası. Değerler bu stackte tutuluyor. Yerel değikenler ise bir dizide tutuluyor. Yukarıdaki kod parçası main metottan çağrılan ve başka bir yerel değişkene sahip olmayan bir metotta olduğundan, yerel değişken olarak sadece int “i” var ve onun değeri de, yerel değişken dizisinin “0” nolu odacığında tutuluyor. Ve olan biten şu: 0. satırda stacke “0” yükleniyor. Sonra bu değer 1. satırla “i”ye atanıyor. 2. satırda ise “i”nin değeri stacke kopyalanıyor ve 3. satırla “i”nin değeri 1 arttırılıyor. Buraya kadar iyi. Fakat bakın 6. satırda, az önce stacke kopyaladığımız “i”nin ilk değeri yani “0”, “i”ye tekrar atanıyor. Dolayısıyla, “i”ye yapılan artım kayboluyor. Olup biteni biraz daha anlaşılır yazarsak aslında 2. satırda yapılan şey “i”nin değerinin bir geçici değişkene atanması ve 6. satırda yapılan da bu geçici değişkendeki değerin “i”ye geri atanması. Yani yukarıdaki kodun şundan farkı yok aslında:
... int i = 0; int temp = i; i++; i = temp; System.out.println(i); ...
Dolayısıyla “i”nin değerinin “0”da kalması normal. Yukarıda verdiğim ve arttırılan “i”nin “j”ye atandığı kodun bytecodeuna da bakarsanız farkı görürsünüz.
Ben bu durumla daha yeni karşılaştım. Çünkü hiç bir zaman basit bir şekilde “i++” ya da “i+=1” ile yapacağım şeyi böyle yapmaya kalkışmamıştım. Ama güzel olan, bir şey daha öğrenmiş olmamız.
C#’ta da durum aynı mıdır acaba?
Toplam görüntülenme sayısı: 1181
Yunus
13 Mayıs 2011 @ 05:17
Evet Hocam c#’da aynı tepkiyi veriyor.
i=0 🙂
Nafiz
13 Mayıs 2011 @ 06:43
int i = 0;
i = ++i;
System.out.println(i);
Bu şekilde kullanırsanız arttırılmış değeri değişkene aktarabilirsiniz.
Akin
13 Mayıs 2011 @ 16:56
Tahmin etmiştim 🙂 Teşekkurler.
Akin
13 Mayıs 2011 @ 17:01
Teşekkür ederim. Bahsettiğiniz zaten “pre-increment” olduğu için onda problem yok. Problem “post-increment”de. Zaten sadece “i++;” yazarsanız yine problem yok. Pek çok kişi bu şekilde yazdığından bu problemle pek karşılaşmamıştır.
ali
28 Mayıs 2011 @ 04:48
Aslında burada bir problem yok. Bilakis böyle olmuyorsa hata olurdu. Çünkü i++ gibi bir ifade, başka bir ifadenin içinde yer alıyorsa, bu önce “başka ifadeyi” çalıştır, sonra i++ yı çalıştır” demektir. Yukarda söylendiği gibi ++i kullanılırsa o zaman da önce i++, sonra kapsayan ifade çalışsın anlamına gelecektir. En azından C syntaksına sahip diller için böyle.