İ = 0 için, neden (i + = i ++) 0'a eşittir?


253

Aşağıdaki kodu alın (Konsol Uygulaması olarak kullanılabilir):

static void Main(string[] args)
{
    int i = 0;
    i += i++;
    Console.WriteLine(i);
    Console.ReadLine();
}

Sonuç i0'dır. Beklediğim 2 (bazı meslektaşlarım gibi). Muhtemelen derleyici isıfır ile sonuçlanan bir tür yapı oluşturur .

Beklediğim 2 sebebi, benim düşünceme göre, sağ el ifadesinin ilk önce i'yi 1 ile artırarak değerlendirilmesidir. İ zaten 1 olduğu için 1'e 1 ekliyor. Yani 1 + 1 = 2. Açıkçası olan şey bu değil.

Derleyicinin ne yaptığını veya çalışma zamanında ne olduğunu açıklayabilir misiniz? Sonuç neden sıfır?

Bazı tür sorumluluk reddi: Bu kodu kullanmayacağınızı (ve muhtemelen kullanmamanız) kesinlikle farkındayım. Asla yapmayacağımı biliyorum. Yine de neden böyle davrandığını ve tam olarak neler olduğunu bilmek ilginç geliyor.


57
beklenen sonuç 1 olmamalı mı? i (0) + = i ++ (1) bu nedenle 0 + = 1 = 1
ittifak

11
Beklendiği gibi çalışıyor
Steve bir D

177
Bu sorunun kaç varyasyonu sorulacak?
mowwwalker

20
Ön girişim i + = ++ kripto parasını yapmadan önce değeri artıracak, size 1 vereceğim
Pierluc SS

21
Neden herkes postincrement öncesi ve postrussus üzerine odaklanıyor? "Garip" şey olmasıdır değeri arasında isol taraftaki +=sağ tarafındaki önce "önbelleğe alınmış" bir değerlendirilir. Bu, örneğin ibir nesne olsaydı bir kopyalama işlemi gerektireceği için sezgiseldir . (Lütfen beni yanlış anlamayın: Bunun 0doğru ve standartlara uygun cevap olduğunu belirtmeyi kesinlikle kabul ediyorum .)
JohnB

Yanıtlar:


425

Bu:

int i = 0;
i += i++

Yaptığınız gibi görülebilir (aşağıdaki brüt aşırı basitleştirme):

int i = 0;
i = i + i; // i=0 because the ++ is a postfix operator and hasn't been executed
i + 1; // Note that you are discarding the calculation result

Aslında olan bundan daha fazlasını içerir - MSDN, 7.5.9 Postfix arttırma ve azaltma operatörlerine bir göz atın :

X ++ veya x-- formunun bir düzeltme sonrası arttırma veya azaltma işleminin çalışma zamanı işlenmesi aşağıdaki adımlardan oluşur:

  • X değişken olarak sınıflandırılırsa:

    • x değişkeni üretmek için değerlendirilir.
    • X değeri kaydedilir.
    • Seçilen operatör argümanı olarak x kaydedilmiş değeri ile çağrılır.
    • Operatör tarafından döndürülen değer, x değerinin değerlendirilmesi ile verilen konumda saklanır.
    • Kaydedilen x değeri işlemin sonucu olur.

Nedeniyle bu Not öncelik sırasına , sonek ++meydana önce += fakat kullanılmayan olmak kadar sonuç uçlarının (önceki değeri olarak ikullanılır).


Bir daha kapsamlı ayrışma i += i++içerisine yapıldığı kısımlarına hem bilmek birini gerektiren +=ve ++atomik değildir (olduğunu, ne biri tek operasyondur) oldukları gibi onlar bakmak bile. Bunların uygulanma şekli geçici değişkenler içerir i, operasyonlar gerçekleşmeden önceki kopyaları - her operasyon için bir tane. (Adları ve sırasıyla iAddve iAssigniçin kullanılan geçici değişkenler için kullanacağım ).+++=

Yani, olanlara daha yakın bir yaklaşım şöyle olacaktır:

int i = 0;
int iAdd = i; // Copy of the current value of i, for ++
int iAssign = i; // Copy of the current value of i, for +=

i = i + 1; // i++ - Happens before += due to order of precedence
i = iAdd + iAssign;

3
@Oded ++operasyon yapılır önce deyim tamamlanıncaya değerlendirilmesi. Böylece +=değerin üzerine yazar. Bu oldu mu?
Anirudh Ramanathan

6
Aslında onun @Oded: int i = 0; i = i + 1; (postfix) i = 0; (assignment). Bu ifadede başka bir yerde i kullandıysanız, o zaman 1 olarak değerlendirilir.
16'da drch

@Cthulhu - Esasen. Cevap tarafından dtb ayrıntılarına girer.
Oded

6
Bunu satın almıyorum. @Yoriy'in cevabı çok daha doğru. Birincisi, cevabınızda, son satırın i+1olması gerektiği gibi olacağını söylüyorsunuz i=i+1. Bu değil i++mi?
12:34 de recluze

3
Cevabın ilk kısmı gereksizdir. Son kod numaranız bunu IMHO yapabilirdi. +1 olsa.
corazza

194

Çalışan kodun sökülmesi:

int i = 0;
  xor         edx, edx
  mov         dword ptr i, edx         // set i = 0
i += i++;
  mov         eax, dword ptr i         // set eax = i (=0)
  mov         dword ptr tempVar1, eax  // set tempVar1 = eax (=0)
  mov         eax, dword ptr i         // set eax = 0 ( again... why??? =\ )
  mov         dword ptr tempVar2, eax  // set tempVar2 = eax (=0)
  inc         dword ptr i              // set i = i+1 (=1)
  mov         eax, dword ptr tempVar1  // set eax = tempVar1 (=0)
  add         eax, dword ptr tempVar2  // set eax = eax+tempVar2 (=0)
  mov         dword ptr i, eax         // set i = eax (=0)

Eşdeğer kod

Aşağıdaki kodla aynı kodu derler:

int i, tempVar1, tempVar2;
i = 0;
tempVar1 = i; // created due to postfix ++ operator
tempVar2 = i; // created due to += operator
++i;
i = tempVar1 + tempVar2;

İkinci kodun sökülmesi (sadece aynı olduklarını kanıtlamak için)

int i, tempVar1, tempVar2;
i = 0;
    xor         edx, edx
    mov         dword ptr i, edx
tempVar1 = i; // created due to postfix ++ operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar1, eax
tempVar2 = i; // created due to += operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar2, eax
++i;
    inc         dword ptr i
i = tempVar1 + tempVar2;
    mov         eax, dword ptr tempVar1
    add         eax, dword ptr tempVar2
    mov         dword ptr i, eax

Sökme penceresini açma

Çoğu kişi, Visual Studio Sökme penceresini kullanarak son bellek içi montaj kodunu görebileceklerini bilmiyor, hatta hatırlamıyor . Yürütülmekte olan makine kodunu gösterir, CIL değildir.

Hata ayıklama sırasında bunu kullanın:

Debug (menu) -> Windows (submenu) -> Disassembly

Peki postfix ++ ile neler oluyor?

Postfix ++, değerlendirmeden sonra işlenenin değerini arttırmak istediğimizi söyler ... herkesin bildiği ... biraz kafa karıştırıcı olan şey "değerlendirmeden sonra" anlamına gelir .

Peki "değerlendirmeden sonra" ne anlama gelir:

  • işlenenin aynı kod satırındaki diğer kullanımları etkilenmelidir:
    • a = i++ + i ikinci i artıştan etkilenir
    • Func(i++, i) ikinci etkilendiğimde
  • gibi aynı hat saygı kısa devre operatör diğer kullanımlar ||ve &&:
    • (false && i++ != i) || i == 0 üçüncü i değerlendirilmediğinden i ++ 'dan etkilenmez

Peki anlamı i += i++;nedir?

Aynı i = i + i++;

Değerlendirme sırası:

  1. Mağaza i + i (0 + 0)
  2. Artış i (i 1 olur)
  3. Adım 1'in değerini i'ye atayın (i 0 olur)

Artışın atılmadığı değil.

Anlamı nedir: i = i++ + i;?

Bu, önceki örnekle aynı değildir. Üçüncüsü artıştan ietkilenir.

Değerlendirme sırası:

  1. Mağaza i (0'dır)
  2. Artış i (i 1 olur)
  3. Adım 1 + i'nin mağaza değeri (0 + 1'dir)
  4. 3. adımın değerini i'ye atayın (i 1 olur)

22
+ 1 ++ - sert hardcore diseksiyonu için. Chuck Norris gurur duyacaktır :) Sanırım OP bir Mono bağlantı noktası değil, Intel üzerinde olduğu varsayımını yapmak ...
StuartLC

19
C # ifadesi için iyi tanımlanmış bir değerlendirme sırasına sahiptir ve nesne kodu bu sırayı uygular. Makine kodu çıktısı, değerlendirme siparişinin nedeni veya açıklaması değildir.
Kaz

8
Makine kodu, değerlendirme sırasının IMO'nun nasıl uygulandığını anlamayı kolaylaştırır.
Kevin

5
@StuartLC Orada ne yaptığınızı görüyorum. Utanmış o oy hakkında utanç olsa.
Steffan Donal

2
a++ + aaynı şey değildir a + a++bu artık saf matematik çünkü. Cebirdeki değişme yasası, değişkenlerin bir ifadenin ortasında değer değiştirmesi olasılığını dikkate almaz. Programlama işlevsel programlama olduğunda matematik sadece programlamaya düzgün şekilde eşlenir. Ve o zaman bile değil, temsili sınırlamalar yüzünden. Örneğin kayan nokta sayıları bazen gerçek gibi davranır, bazen de değildir. Matematikteki gerçek sayılar için geçerli olan yan etkiler, değişme ve ilişkilendirilebilirlik yasaları olmasa bile kayan noktalı sayılar üzerinde.
Kaz

61
int i = 0;
i += i++;

aşağıdaki gibi değerlendirilir:

Stack<int> stack = new Stack<int>();
int i;

// int i = 0;
stack.Push(0);                   // push 0
i = stack.Pop();                 // pop 0 --> i == 0

// i += i++;
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(1);                   // push 1
i = stack.Pop() + stack.Pop();   // pop 0 and 1 --> i == 1
i = stack.Pop() + stack.Pop();   // pop 0 and 0 --> i == 0

ie iiki kez değiştirilir: bir kez i++ifade ile ve bir kez +=ifade ile.

Ancak +=ifadenin işlenenleri

  • ideğerlendirilmeden önceki değeri i++(sol tarafı +=) ve
  • ideğerlendirilmesinden önceki değer i++(sağ tarafı +=).

Ah bu harika bir açıklama. Ters lehçe gösterimi kullanarak yığın tabanlı bir hesap makinesinde çalıştığımı hatırlatıyor.
Nathan

36

Önce i++0 döndürür. Sonra i1 artar. Son olarak ibaşlangıç ​​değeri i0 olan artı i++döndürülen değer olan sıfır olarak ayarlanır . 0 + 0 = 0.


2
Ancak i += i++;, değil i = i++;, bu nedenle i++(0) değeri "döndürülen değere ayarlandı i" iolarak eklenir ve eklenmez i++. Değer eklerken Şimdi soru, bir i++iade i, olacak iarttırılmış değer ya da değil-artırılır değer? Cevap, dostum, şartnamelerde yazılmıştır.
Daniel Fischer

Doğru, tamir edeceğim. Ama yine de bu yana i = 0başlangıçta i += somethingeşdeğerdir i = 0 + somethingolan i = something.
Jong

32

Bu, soyut sözdizimi ağacının soldan sağa, aşağıdan yukarıya değerlendirmesi. Kavramsal olarak, ifadenin ağacı yukarıdan aşağıya doğru yürür, ancak özyineleme ağacı aşağıdan yukarıya doğru açtıkça değerlendirme başlar.

// source code
i += i++;

// abstract syntax tree

     +=
    /  \
   i    ++ (post)
         \
         i

Değerlendirme kök düğümü dikkate alınarak başlar +=. Bu ifadenin ana bileşenidir. Sol işleneni +=, değişkeni depoladığımız yeri belirlemek ve sıfır olan önceki değeri elde etmek için değerlendirilmelidir. Daha sonra, sağ taraf değerlendirilmelidir.

Sağ taraf artımlı bir ++operatördür. iHem bir değerin kaynağı hem de değerin saklanacağı bir yer olarak değerlendirilen bir işleneni vardır . Operatör değerlendirir i, bulur 0ve sonuç 1olarak bu konumu bir a depolar . Önceki değeri 0döndürme anlamlarına uygun olarak önceki değeri döndürür.

Şimdi kontrol +=operatöre geri döndü . Artık çalışmasını tamamlamak için tüm bilgilere sahip. Sonucun depolanacağı yeri (saklama yeri i) ve önceki değeri bilir ve önceki değere eklenecek değere sahiptir 0. Yani, isıfır ile biter.

Java gibi, C # da değerlendirme sırasını düzelterek C dilinin çok asinine bir yönünü temizledi. Soldan sağa, aşağıdan yukarıya: kodlayıcılar tarafından beklenen en açık düzen.


: 1: Ben beklediğinin her kodlayıcı ... Ben böyle bir şey ile aynı olması bekleniyor olduğu dışında size katılıyorum SetSum(ref i, Inc(ref i))ile int SetSum(ref int a, int b) { return a += b; }ve int Inc(ref int a) { return a++; }ben artık bekliyoruz tabii ....
Miguel Angelo

Ayrıca, beklediğim tutarsız! Ve Set(ref i, Sum(i, Inc(ref i)))ile eşit olmaz . int Set(ref int a, int b) { return a = b; }int Sum(int a, int b) { return a + b; }
Miguel Angelo

Teşekkürler; cevabımda düzeltmem gereken bir kusur / eksikliğe işaret ediyorsun.
Kaz

Sorun SetSumşu ki, sol işleneni değerlendirmiyor i, ancak sadece adresini alıyor, bu yüzden işlenenin tam bir soldan sağa değerlendirmesine eşdeğer değil. Gibi bir şeye ihtiyacın var SetSum(ref i, i, PostInc(ref i)). İkinci argümanı, SetSumyalnızca iönceki değerini belirtmek için kullandığımız eklenecek değerdir i. SetSumsadece int SetSum(ref int dest, int a, int b) { return dest = a + b; }.
Kaz

Karışıklık (en azından benim için) + = operatörü ile olur, çünkü atama operatörü sağdan sola değerlendirmeye sahiptir (örneğin a = b = c = d) ... böylece + = aynı kuralı izleyebilir, Atomik bir işlem olarak (SetSum yöntemimle yaptığım gibi) ... ama gerçekte olan şey C # 'a a += bdönüşüyor a = a + b... + = operatörünün atomik olmadığını ... sadece sözdizimsel şeker olduğunu gösteriyor.
Miguel Angelo

30

Çünkü i++önce değeri döndürür, sonra artırır. Ama i 1 olarak ayarlandıktan sonra tekrar 0 olarak ayarladınız.


17

Artış sonrası yöntemi şuna benzer

int ++(ref int i)
{
    int c = i;
    i = i + 1;
    return c;
}

Yani temelde Aradığınızda i++, iartım ama orijinal değeri 0 iade ediliyor senin durumunda döndürülür.


12

Basit cevap

int i = 0;
i += i++;
// Translates to:
i = i + 0; // because post increment returns the current value 0 of i
// Before the above operation is set, i will be incremented to 1
// Now i gets set after the increment,
// so the original returned value of i will be taken.
i = 0;

12

i ++ şu anlama gelir: i THEN değerini arttırır.

i + = i ++ şu anlama gelir: i'nin geçerli değerini alır. İ ++ sonucunu ekleyin.

Şimdi, başlangıç ​​koşulu olarak i = 0 ekleyelim. i + = i ++ şimdi şu şekilde değerlendirilir:

  1. İ'nin şu anki değeri nedir? 0'dır. İ ++ sonucunu buna ekleyebilmemiz için saklayın.
  2. İ ++ değerini değerlendirin (i'nin geçerli değeri olduğu için 0 olarak değerlendirilir)
  3. Depolanan değeri yükleyin ve 2. adımın sonucunu buna ekleyin. (0'dan 0'a ekleyin)

Not: 2. adımın sonunda, i değeri aslında 1'dir. Ancak, 3. adımda, i değerini artırılmadan önce yükleyerek atarsınız.

İ ++ 'ın aksine, ++ i artan değeri döndürür.

Bu nedenle, i + = ++ size 1 veririm.


Bu tam
sonsha

11

Düzeltme sonrası artış operatörü, ++değişkene ifadede bir değer verir ve sonra atadığınızi artışı sıfır (0) değerini , artırılan bir (1) üzerine yazan yeniden yapar , böylece sıfır elde edersiniz. Artım operatörü hakkında daha fazla bilgiyi ++ Operatöründe (MSDN) okuyabilirsiniz .


8

i += i++;sıfıra eşit olacaktır, çünkü ++daha sonra yapar .

i += ++i; daha önce yapacak


4
Daha sonra yaparsa ++, sonucun olmasını beklerdim 1.
comecme

8

++ postfix i, artırılmadan önce değerlendirir ve +=yalnızca bir ikez değerlendirir .

Bu nedenle 0 + 0 = 0 olarak ideğerlendirilir ve artırılmadan önce kullanılır, postfix formatı ++kullanılır. Almak için iilk artırılır, önek formu kullanın ( ++i).

(Ayrıca, sadece bir not: 0 + (0 + 1) = 1 olarak sadece 1 almalısınız)

Kaynaklar: http://msdn.microsoft.com/en-us/library/sa7629ew.aspx (+ =)
http://msdn.microsoft.com/en-us/library/36x43w8w.aspx (++)


8

C # ne yapıyor ve karışıklığın "nedeni"

Ben de değerin 1 olmasını bekliyordum ... ama bu konudaki bazı keşifler bazı noktaları açıklığa kavuşturdu.

Aşağıdaki yöntemleri düşünün:

    static int SetSum(ref int a, int b) { return a += b; }

    static int Inc(ref int a) { return a++; }

Onunla i += i++aynı olmasını bekliyordum SetSum(ref i, Inc(ref i)). Bu ifadeden sonra i değeri 1 :

int i = 0;
SetSum(ref i, Inc(ref i));
Console.WriteLine(i); // i is 1

Ama sonra başka bir sonuca vardım ... i += i++aslında aynı i = i + i++... bu yüzden bu işlevleri kullanarak başka bir benzer örnek oluşturdum:

    static int Sum(int a, int b) { return a + b; }

    static int Set(ref int a, int b) { return a = b; }

Bunu çağırdıktan sonra Set(ref i, Sum(i, Inc(ref i)))i değeri 0 olur :

int i = 0;
Set(ref i, Sum(i, Inc(ref i)));
Console.WriteLine(i); // i is 0

Bu sadece C # 'ın ne yaptığını açıklamakla kalmıyor, aynı zamanda birçok insanın neden onunla karıştığını da açıklıyor ... ben de dahil.


2
Lütfen bunu orijinal cevabınıza ekleyin, ayrı bir cevap olarak almanın faydası yoktur.
casperOne

2
Bunu diğer cevabı yok etmek için yapmadım, çünkü kod çözülmüş kodla ilgili ... bu sırada şeyleri açıklamak için farklı bir yaklaşım denedim. Ne düşünüyorsun? Diğer cevabı düzenlemeli ve bu cevabı eklemeli miyim? Belki, bunun başına ... bilmiyorum! Önerileriniz için teşekkürler!
Miguel Angelo

7

Her zaman hatırladığım iyi bir anımsatıcı şudur:

Eğer ++duruyor sonra ifadesi, buna oldu değerini döndürür önce . Yani aşağıdaki kod

int a = 1;
int b = a++;

Çünkü, 1'dir a1 oldu önce o artarak var ++ayakta sonra a . İnsanlar bu gönderi düzeltme gösterimi diyoruz . Bir de bulunmaktadır öncesi şeyler tam zıddı olan düzeltme notasyonu,: eğer ++standları önce , ifadesi o olduğunu değerini döndürür sonra operasyon:

int a = 1;
int b = ++a;

b burada iki.

Yani kodunuz için bu,

int i = 0;
i += (i++);

i++0 değerini döndürür (yukarıda açıklandığı gibi) 0 + 0 = 0.

i += (++i); // Here 'i' would become two

Scott Meyers , "Etkili C ++ programlaması" ndaki bu iki gösterim arasındaki farkı tanımlar. Dahili olarak i++(postfix) değeri iolduğunu hatırlar ve prefix-notation ( ++i) öğesini çağırır ve eski değeri döndürür i. Eğer daima kullanmalısınız nedeni budur ++iiçinde for(ı tüm modern derleyiciler çevirmekte olduğunuz düşünüyorum rağmen döngüler i++için ++ide fordöngüler).


1
Test ettim int i = 0; i += (++i)ve iikiden ziyade bire ayarlandım. Bu yazarsanız, gerçeğini değiştirmez yerine postfix öneki kullanılarak beri, bana da mantıklı i += (++i)dışarı i = i + (++i), iönce değerlendirilir ++isonuçlanan i = 0 + (++i)ve sonuçta i = 0 + 1.
Wutz

6

Sorunuza doğru olan tek cevap şudur: Çünkü tanımsızdır.

Tamam, hepiniz beni yakmadan önce ..

Hepiniz bunun neden i+=i++iyi ve mantıklı olduğunu söylediniz i=0.

Cevaplarınızın her birini aşağıya oylamaya cazip geldim ama hesapladığım itibar çok yüksek olurdu.

Neden sana bu kadar kızgınım insanlar? Cevaplarınızın açıkladığı için değil ..
Yani, okuduğum her cevap imkansızı açıklamak için kayda değer bir çaba sarf etti, Alkışlar!

Ama sonuç nedir ?? sezgisel bir sonuç mu - kabul edilebilir bir sonuç mu ??

Her biriniz "çıplak kralı" gördünüz ve bir şekilde rasyonel kral olarak kabul ettiniz.

Hepiniz YANLIŞsınız!

i+=i++; sonuçlanmak 0 tanımsız.

dil değerlendirme mekanizmasında bir hata ... ya da daha kötüsü! tasarımda bir hata.

kanıt ister misin? tabii ki istiyorsun!

int t=0; int i=0; t+=i++; //t=0; i=1

Şimdi bu ... sezgisel bir sonuç! çünkü önce değerlendirdikt olarak bir değerle değerlendirdik ve ancak değerlendirme ve görevlendirmeden sonra operasyon sonrası gerçekleştik - rasyonel değil mi?

rasyonel mi: i=i++ve i=iaynı sonucu vermek içini midir ?

süre t=i++ve t=iiçin farklı sonuçlar var i.

Post operasyon ifadenin değerlendirilmesinden sonra olması gereken bir şeydir.
Bu nedenle:

int i=0;
i+=i++;

Eğer yazsaydık aynı olmalı:

int i=0;
i = i + i ++;

ve bu nedenle aynı:

int i=0;
i= i + i;
i ++;

ve bu nedenle aynı:

int i=0;
i = i + i;
i = i + 1;

1Rasyonel düşünmeye devam edersek, karşılaştırıcıda bir hata veya dil tasarımında bir hata olduğunu göstermeyen herhangi bir sonuç - ancak MSDN ve diğer birçok kaynak bize "hey - bu tanımsız!"

Şimdi, devam etmeden önce, verdiğim bu örnekler bile hiç kimse tarafından desteklenmiyor ya da kabul edilmiyor. Ancak sezgisel ve rasyonel yolla sonuç bu olmalıydı.

Kodlayıcı, montajın nasıl yazıldığını veya tercüme edildiğini bilmemelidir!

Dil tanımlarına uymayacak şekilde yazılmışsa - bu bir hatadır!

Ve bitirmek için bunu Wikipedia'dan kopyaladım , Arttırma ve azaltma operatörleri :
Artış / azaltma operatörü işlenenini değiştirdiğinden, böyle bir işlenenin aynı ifadede birden çok kez kullanılması tanımlanmamış sonuçlar üretebilir . Örneğin, x - ++ x gibi ifadelerde, çıkarma ve artış işleçlerinin hangi sırayla gerçekleştirilmesi gerektiği açık değildir. Derleyici tarafından optimizasyonlar uygulandığında bu gibi durumlar daha da kötüleşir, bu da işlemlerin yürütme sırasının programcının amaçladığından farklı olmasına neden olabilir.

Ve bu nedenle.

Doğru cevap, bu KULLANILMAMALIDIR! (TANIMLANMADIĞI gibi!)

Evet .. - C # complier bir şekilde normalleştirmeye çalışsa bile öngörülemeyen sonuçları var.

Hepinizin dilin normal veya iyi tanımlanmış bir davranışı olarak belgelenen davranışı tanımlayan herhangi bir C # belgesi bulamadım. Ne buldum tam tersi!

[ Postfix Arttırma ve Azaltma İşleçleri için MSDN belgelerinden kopyalandı: ++ ve - ]

Bir işlev bağımsız değişkenine postfix operatörü uygulandığında, bağımsız değişkenin değerinin işleve geçirilmeden önce artırılması veya azaltılması garanti edilmez . Daha fazla bilgi için C ++ standardındaki 1.9.17 bölümüne bakın.

Bu kelimelerin garanti edilmediğine dikkat edin ...

Bu cevap kibirli görünüyorsa affet beni - kibirli biri değilim. Sadece binlerce insanın buraya öğrenmeye geldiğini ve okuduğum yanıtların onları yanlış yönlendireceğini ve konuyla ilgili mantıklarına ve anlayışlarına zarar vereceğini düşünüyorum.


% 100 takip ettiğimden emin değilim, ancak C ++ belgelerine başvuruyorsunuz, ancak sorum C # hakkındaydı. Bununla ilgili belgeler burada .
Peter

Cevabımda C # 'a atıfta bulunuyordum. Sağladığınız bağlantıdan: x ++ veya x-- sonucu, işlemden önce x değeri, ++ x veya --x sonucu ise işlemden sonra x değeridir. Her iki durumda da, x'in kendisi işlemden sonra aynı değere sahiptir. Test sırasında durumun bu olmadığını açıkça göstermektedir .. çünkü i=++ifarklı sonuçlar sağlayacaktır i=i++. Bu yüzden cevabım duruyor.
GY

Aha, tamam, ama C ++ belgelerine başvururken kafa karıştırıcı. Yani, söylediğiniz şey şartnamenin doğru bir şekilde uygulanmadığı mı?
Peter

Hayır. Söylediğim, spesifikasyona göre tanımsız olması ve tanımsız kullanmanın tanımsız sonuçlarla sonuçlanmasıdır.
GY

C ++ 'da tanımlanmamış, ancak C # , işlemden sonra aynı değerde olması gerektiğini söylüyor , değil mi? Bu undefined ile aynı değil (ama bunu kullanmamanız gerektiğine katılıyorum, yasal uyarımı görün, sadece neler olduğunu anlamaya çalışıyordum).
Peter

4

Değişkenten sonraki ++ işleci, onu bir postfix artışı yapar. Artış, ifadedeki, ekleme ve atamadaki diğer her şeyden sonra gerçekleşir. Bunun yerine, ++ değişkeninden önce koyarsanız, i değeri değerlendirilmeden önce gerçekleşir ve size beklenen yanıtı verir.


2
Açıklamadan sonra++ gerçekleşmez , ifadenin yürütülmesi sırasında olur . Bu yüzden get tarafından geçersiz kılmanın etkileri . +=+=+++=
dtb

++ i kullanmak aslında 1 değil, 2'de sonuçlanır (başlangıçta 'beklenen cevap').
Peter

Görünüşe göre atama , ifadedeki artış öncesi veya sonrası değişikliklerin += üzerine yazıyor.
Steven Lu

4

Hesaplama adımları:

  1. int i=0 // 0 olarak başlatıldı
  2. i+=i++ //Denklem
  3. i=i+i++ // denklemi derleyici ile basitleştirdikten sonra
  4. i=0+i++ // ikame değeri
  5. i=0+0 // i ++ aşağıda açıklandığı gibi 0'dır
  6. i=0 // Nihai sonuç i = 0

Burada, başlangıçta değeri i0'dır. WKT, i++başka bir şey değildir: önce ideğeri kullanın ve sonra ideğeri 1 artırın. Bu nedenle i, hesaplanırken 0 değerini kullanır i++ve daha sonra 1 arttırır. 0.


3

İki seçenek vardır:

İlk seçenek: derleyici ifadeyi aşağıdaki gibi okursa,

i++;
i+=i;

sonuç 2 olur.

İçin

else if
i+=0;
i++;

sonuç 1'dir.


5
Bunların hiçbiri gerçek sonuç değildir.
Steven Lu

3

Çok dikkatli olun: C SSS'sini okuyun : yapmaya çalıştığınız şey (karıştırma ataması ve ++aynı değişkenin) sadece tanımlanmamış değildir, aynı zamanda tanımlanmamıştır (yani derleyici değerlendirirken her şeyi yapabilir !) "reasonnable" sonuçları).

Lütfen 3. bölümü okuyunuz . Bütün bölüm iyi bir okumaya değer! Özellikle 3.9, hangi belirtilmemiş anlamına gelir açıklar. Bölüm 3.3 size "i ++" ve benzerleri ile yapabilecekleriniz ve yapamayacağınız şeylerin kısa bir özetini verir.

Derleyicilerin iç kısımlarına bağlı olarak, 0, 2 veya 1, hatta başka bir şey alabilirsiniz! Ve tanımsız olduğu için, bunu yapmaları sorun değil.


ayy, c # ... "gcc" tarafından atıldım, bazıları kodu sökmeye başladı.
Olivier Dulac

1
Ben de C # olduğunu cevapsız, ama yine de cevap sevdim.
Iain Collins

1
@Iain: teşekkürler, ben de cevabı mevcut tutmaya değer olduğuna inanıyorum, birçok kişi bunu (ya da bu büyük sss hakkında bilmiyor , Usenet'in bir subjet hakkında bilgi sahibi olan insanların çoğunun aynı olacağı en iyi zamanından güncellemek için yer)
Olivier Dulac

3

Yukarıdaki cevaplarda çok iyi akıl yürütme var, sadece küçük bir test yaptım ve sizinle paylaşmak istiyorum

int i = 0;
i+ = i++;

Burada i sonucu 0 sonucunu gösteriyor. Şimdi aşağıdaki durumları düşünün:

Dava 1:

i = i++ + i; //Answer 1

daha önce yukarıdaki kod buna benzediğini düşündüm, bu yüzden ilk bakışta cevap 1, ve gerçekten bu i için 1 cevap.

Durum 2:

i = i + i++; //Answer 0 this resembles the question code.

burada artış operatörü i ++ eklemeden önce yürütme şansına sahip olduğu önceki durumdan farklı olarak yürütme yoluna gelmez.

Umarım bu biraz yardımcı olur. Teşekkürler


2

Bunu bir C programlama 101 tipi perspektiften cevaplamayı umuyorum.

Bana bu sırayla oluyormuş gibi geliyor:

  1. i0 olarak değerlendirilir ve "sıraya alınmış" i = 0 + 0artış işlemiyle sonuçlanır i++, ancak 0'ın atanması ida henüz gerçekleşmez.
  2. Artış i++meydana gelir
  3. Yukarıdaki atama i = 0, # 2'nin (artım sonrası) yapacağı her şeyin üzerine etkili bir şekilde yazarak gerçekleşir.

Şimdi, # 2 hiçbir zaman gerçekleşmeyebilir (muhtemelen değil?) Çünkü derleyici muhtemelen hiçbir amaca hizmet edemeyeceğini fark eder, ancak bu derleyiciye bağlı olabilir. Her iki durumda da, daha bilgili diğer cevaplar sonucun doğru olduğunu ve C # standardına uygun olduğunu göstermiştir, ancak burada C / C ++ için ne olduğu tanımlanmamıştır.

Nasıl ve neden uzmanlığımın ötesinde, ancak daha önce değerlendirilen sağ taraf görevlendirmesinin artımdan sonra gerçekleşmesi muhtemelen burada kafa karıştırıcı olan şeydir.

Dahası, ne yaptığını sürece sonuç ne olursa olsun 2 olmasını bekliyoruz olmaz ++iyerine i++inanıyorum.


1
Ön sürüm, 2C ++ ile bir sonuç üretir : ideone.com/8dH8tf
Steven Lu

Mantıklı. Ancak ön artış, artış sonrasıkinden daha az karmaşık bir durumdur.
gkimsey

2

Basit ifadeyle,

i ++, "+ =" operatörü tamamlandıktan sonra "i" ye 1 ekler.

"+ =" Operatörü çalıştırılmadan önce "i" ye 1 ekleyecek şekilde istediğiniz ++ i.


0
i=0

i+=i

i=i+1

i=0;

Sonra 1 eklenir i.

i + = i ++

Yani i, 1 eklemeden önce i0 değerini aldı. Sadece 1 eklersek, i0 değerini alın.

i+=++i

i=2

-4

Cevap iolacak 1.

Nasıl yapılacağına bakalım:

Başlangıçta i=0;.

Daha sonra i +=i++;değere göre hesaplama yaparken benzer bir şeye sahip olacağız 0 +=0++;, bu yüzden operatörün önceliğine göre önce 0+=0performans gösterecek ve sonuç olacaktır 0.

Daha sonra artış operatörü olarak uygulanır olacak 0++şekilde, 0+1ve değeri iolacaktır 1.


3
Bu cevap yanlış. 0 += 0++;Atama yaptığınızda ++++
artıştan

2
Üzgünüm, ama bu yanlış. Sorumu okuyun ve sonuç 0 olduğunu söyleyeceğim. Kodu çalıştırırsanız, etkin bir şekilde 0 olduğunu görürsünüz.
Peter
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.