X = x ++ neden tanımsız?


19

Tanımlanmadı çünkü xsıra noktaları arasında iki kez değişiklik yapıyor. Standart tanımsız olduğunu, bu nedenle tanımsız olduğunu söylüyor.
O kadarını biliyorum.

Ama neden?

Anladığım kadarıyla, bunun yasaklanması derleyicilerin daha iyi optimizasyon yapmasına izin veriyor. Bu, C icat edildiğinde mantıklı olabilirdi, ama şimdi zayıf bir argüman gibi görünüyor.
Bugün C'yi yeniden icat edersek, bunu bu şekilde yapar mıydık yoksa daha iyi yapılabilir mi?
Ya da belki bu tür ifadeler için tutarlı kurallar tanımlamayı zorlaştıran daha derin bir sorun var, bu yüzden onları yasaklamak en iyisidir?

Diyelim ki bugün C'yi yeniden keşfedeceğiz. x=x++Mevcut kurallardan daha iyi çalıştığım gibi ifadeler için basit kurallar önermek istiyorum .
Mevcut kurallar veya diğer önerilerle karşılaştırıldığında önerilen kurallar hakkında görüşlerinizi almak istiyorum.

Önerilen Kurallar:

  1. Dizi noktaları arasında, değerlendirme sırası belirtilmemiştir.
  2. Yan etkiler hemen gerçekleşir.

Tanımlanmamış bir davranış söz konusu değildir. İfadeler bu değere veya buna göre değerlendirilir, ancak sabit diskinizi kesinlikle biçimlendirmez (garip bir şekilde, sabit diski biçimlendiren bir uygulama görmedim x=x++).

Örnek İfadeler

  1. x=x++- İyi tanımlanmış, değişmez x.
    İlk olarak, xartırılır ( x++değerlendirildiğinde hemen ), daha sonra eski değeri saklanır x.

  2. x++ + ++x- xİki kat artar , değerlendirir 2*x+2.
    Her iki taraf da ilk önce değerlendirilebilse de, sonuç x + (x+2)(önce sol taraf) veya (x+1) + (x+1)(önce sağ taraf) olur.

  3. x = x + (x=3)- Belirtilmemiş, xya x+3da olarak ayarlayın 6.
    Önce sağ taraf değerlendirilirse, olur x+3. İlk x=3olarak değerlendirilmesi de mümkündür , bu yüzden 3+3. Her iki durumda da, x=3ödev x=3değerlendirildiğinde hemen gerçekleşir , böylece saklanan değerin üzerine diğer ödev yazılır.

  4. x+=(x=3)- İyi tanımlanmış, x6'ya ayarlanır .
    Bunun yukarıdaki ifade için sadece kısayol olduğunu iddia edebilirsiniz.
    Ancak bunun iki bölümden +=sonra x=3değil, sonradan yürütülmesi gerektiğini söyleyebilirim ( yeni değeri okuyun x, değerlendirin x=3, ekleyin ve saklayın).

Avantajı Nedir?

Bazı yorumlar bu iyi noktayı dile getirdi.
Kesinlikle x=x++herhangi bir normal kodda kullanılması gerektiği gibi ifadeler sanmıyorum .
Aslında çok daha sıkı daha değilim - Ben sadece iyi kullanımını düşünmek x++olarak x++;yalnız.

Ancak, dil kurallarının olabildiğince basit olması gerektiğini düşünüyorum. Aksi takdirde programcılar onları anlamıyor. sıra noktaları arasında bir değişkeni iki kez değiştirmeyi yasaklayan kural, çoğu programcının anlamadığı bir kuraldır.

Çok temel bir kural şudur:
A geçerliyse ve B geçerliyse ve geçerli bir şekilde birleştirildiyse, sonuç geçerlidir.
xgeçerli bir L-değeri, x++geçerli bir ifade ve =bir L-değeri ile bir ifadeyi birleştirmenin geçerli bir yoludur, peki nasıl olur x=x++yasal değil?
C standardı burada bir istisna oluşturur ve bu istisna kuralları karmaşıklaştırır. Stackoverflow.com'da arama yapabilir ve bu istisnanın insanları ne kadar karıştırdığını görebilirsiniz.
Diyorum ki - bu karışıklıktan kurtulun.

=== Cevapların Özeti ===

  1. Neden bunu yapıyorsun?
    Yukarıdaki bölümde açıklamaya çalıştım - C kurallarının basit olmasını istiyorum.

  2. Optimizasyon potansiyeli:
    Bu derleyiciden biraz özgürlük gerektiriyor, ancak beni önemli olabileceğine ikna eden hiçbir şey görmedim.
    Çoğu optimizasyon hala yapılabilir. Örneğin a=3;b=5;, standart siparişi belirtmesine rağmen yeniden sıralanabilir. Gibi ifadeler a=b[i++]yine de benzer şekilde optimize edilebilir.

  3. Mevcut standardı değiştiremezsiniz.
    Kabul ediyorum, yapamam. Aslında devam edip standartları ve derleyicileri değiştirebileceğimi hiç düşünmemiştim. Sadece işler farklı yapılabilirse düşünmek istedim.


10
Bu senin için neden önemli? Meli o tanımlanır ve eğer öyleyse, neden olabilir? xKendisine atamanın fazla bir anlamı yoktur ve eğer arttırmak xistiyorsanız sadece söyleyebilirsiniz x++;- ödeve gerek yoktur. Sadece ne olması gerektiğini hatırlamak zor olacağı için tanımlanmaması gerektiğini söyleyebilirim .
Caleb

4
Aklımda, bu iyi bir soru ("Bazı insanlar olayları oldukları gibi görüyor ve nedenini soruyor, hiç olmayan şeyleri hayal ediyorum ve neden olmasın diye soruyorum"). (Bence) örnek olarak C sözdizimini kullanan bir dil değil, C sözdizimi ile ilgili bir soru değil. Ben şahsen, x ++ + ++ x veya x = x ++ gibi ifadeler için davranış tanımlamamamızın sebebinin sadece yanlış okunma olasılığı olduğu için düşünüyorum.
Jamie Taylor

5
@ugoren: Neden sonucu tahmin etmeniz gerekiyor? Akıllarında hiç kimse böyle bir kod yazmazdı (birkaç kez bahsedildiği gibi), böyle bir kod yazmış olsanız bile, ilk kod incelemesinde reddedilirdi. Bu nedenle, davranışı tanımlamaya ve optimize ediciyi optimize etme şansına sahip olmaya gerek yoktur. Her örnekte, bunu kod tabanına eklediyse birisini vurmamı önerirsiniz.
Martin York

3
Bunu yazmak için bir hata neden olmasın diye daha ilginç bir soru bulurum? Elbette bir derleyici tanımsız davranışını algılayabilir ve bu nedenle kullanıcının gerçekten istediği şey olamaz, bu yüzden neden bir hatada değil? Tanımsız bahavourun bazı örneklerini tespit etmenin zor olduğunu anlıyorum ama bu değil; t
JohnB

3
" sekans noktaları arasında bir değişkeni iki kez değiştirmeyi yasaklayan kural kesinlikle çoğu programcının anlamadığı bir kuraldır. " - Bunun için kanıtınız var mı? Gördüğüm çoğu soru soruldu çünkü programcılar bu kuralı bilmiyorlardı . Çoğu hala vermedi herhangi bir kanıt var mı anlamaya Tarif edildiği sonra?
elde edin

Yanıtlar:


24

Belki önce neden tanımlanması gerektiği sorusunu cevaplamalısınız? Ek yan etkilerle bu tür ifadelere izin vererek programlama tarzı, okunabilirlik, sürdürülebilirlik veya performansta herhangi bir avantaj var mı? Dır-dir

y = x++ + ++x;

daha okunabilir

y = 2*x + 2;
x += 2;

Böyle bir değişikliğin son derece temel olduğu ve mevcut kod tabanının kırıldığı göz önüne alındığında.


1
Soruma bir "neden" bölümü ekledim. Kesinlikle bu ifadeleri kullanmanızı önermiyorum, ama bir ifadenin anlamını anlatmak için basit kurallara sahip olmakla ilgileniyorum.
ugoren

Ayrıca, tanımsız davranış çağrılmadığı sürece bu değişiklik varolan kodu bozmaz. Yanlışsam düzelt.
ugoren

3
Daha felsefi bir cevap: Şu anda tanımsız. Hiçbir programcı bunu kullanmazsa, bu tür ifadeleri anlamanıza gerek yoktur, çünkü herhangi bir kod olmamalıdır. Onları anlamanız için bir ihtiyaç varsa, açıkçası tanımlanmamış davranışa dayanan çok fazla kod olması gerekir. ;)
Güvenli

1
Tanım olarak, davranışları tanımlamak için mevcut herhangi bir kod tabanını bozmaz. Eğer UB içeriyorlarsa, tanım gereği zaten kırılmışlardı.
DeadMG

1
@ugoren: "Neden" bölümünüz hala pratik soruyu yanıtlamıyor: Kodunuzda bu garip ifadeyi neden istiyorsunuz? Eğer buna ikna edici bir cevap bulamazsanız, tüm tartışma tartışmalıdır.
Mike Baranczak

20

Bu tanımsız davranışın daha iyi optimizasyona izin verdiği iddiası bugün zayıf değildir . Aslında, bugün C'nin yeni olduğu zamandan çok daha güçlü .

C yeniyken, daha iyi optimizasyon için bundan yararlanabilecek makineler çoğunlukla teorik modellerdi. İnsanlar, derleyicinin CPU'ya diğer talimatlara paralel olarak hangi talimatların uygulanabileceği / yapılması gerektiği konusunda talimat vereceği CPU oluşturma olasılığı hakkında konuşmuştu. Bunun tanımlanamayan bir davranışa sahip olmasına izin vermenin, böyle bir CPU'da gerçekten var ise, talimatın "artış" kısmını talimat akışının geri kalanıyla paralel olarak yürütülmesini planlayabileceğinizi belirtmişlerdir. Teori hakkında haklı olsalar da, o zaman bu olasılıktan gerçekten yararlanabilecek donanım yolunda çok az şey vardı.

Artık sadece teorik değil. Şimdi üretimde ve geniş kullanımda (örneğin Itanium, VLIW DSP'ler) bundan gerçekten yararlanabilecek donanımlar var . Onlar gerçekten yapmak derleyici belirtir o talimatlar X, Y ve Z her paralel olarak çalıştırılabilir olduğu bir komut akışını oluşturmasını sağlar. Bu artık teorik bir model değil - gerçek işte gerçek kullanımda gerçek donanım.

IMO, bu tanımlanmış davranışı en kötüsüne yakın yapıyor sorunun "çözüm" . Böyle ifadeleri açıkça kullanmamalısınız. Kodun büyük çoğunluğu için, ideal davranış derleyicinin bu tür ifadeleri tamamen reddetmesi olacaktır. O zaman, C derleyicileri bunu güvenilir bir şekilde tespit etmek için gerekli akış analizini yapmadı. Orijinal C standardı zamanında bile, hala yaygın değildi.

Bugün toplum için de kabul edilebilir olacağından emin değilim - birçok derleyici bu tür bir akış analizi yapabilirken, genellikle yalnızca optimizasyon istediğinizde bunu yapar. Çoğu programcı sadece (aklı başında olmak) asla ilk etapta yazmak olmaz kodu reddetmek uğruna "hata ayıklama" yapıları yavaşlatma fikri istiyorum şüpheliyim.

C'nin yaptığı yarı makul ikinci en iyi seçimdir: insanlara bunu yapmamalarını söyleyin, derleyicinin kodu reddetmesine izin verin (ancak gerektirmez). Bu, daha önce hiç kullanmayan insanlar için derlemeyi yavaşlatmayı önler (yine de), ancak yine de birilerinin isterse bu kodu reddedecek bir derleyici yazmasına izin verir (ve / veya insanların kullanmayı seçebilecekleri reddedecek bayrakları varsa) veya uygun gördükleri gibi değil).

En azından IMO, bu tanımlanmış davranışı yapmak (en azından ona yakın) en kötü karar olacaktır. VLIW tarzı donanımda, artan işleçlerin makul kullanımları için, yalnızca onları kötüye kullanan crappy kodu uğruna, ya da her zaman uğraşmadığınızı kanıtlamak için kapsamlı akış analizine ihtiyaç duymak için daha yavaş bir kod oluşturmak olacaktır. böylelikle yavaş (seri hale getirilmiş) kodu yalnızca gerçekten gerekli olduğunda üretebilirsiniz.

Alt satır: Bu sorunu çözmek istiyorsanız, ters yönde düşünüyor olmalısınız. Böyle bir kodun ne yaptığını tanımlamak yerine, dili tanımlamanız gerekir, böylece bu tür ifadelere hiç izin verilmez (ve çoğu programcının muhtemelen bu gereksinimi zorlamak yerine daha hızlı derlemeyi tercih edeceği gerçeğiyle yaşamak).


IMO, çoğu durumda, yavaş talimatların hızlı talimatlardan çok daha yavaş olduğuna ve bunların her zaman program performansı üzerinde etkisi olacağına inanmak için çok az neden vardır. Bunu erken optimizasyon altında sınıflandırırdım.
DeadMG

Belki bir şey eksik - kimsenin böyle bir kod yazması gerekmiyorsa, neden optimize etmeyi umursuyorsunuz?
ugoren

1
@ugoren: a=b[i++];(bir örnek için) gibi bir kod yazmak iyidir ve optimize etmek iyi bir şeydir. Ancak, böyle makul bir kodu zarar verme noktasını görmüyorum, böyle bir şeyin ++i++tanımlanmış bir anlamı vardır.
Jerry Coffin

2
@ugoren Sorun teşhis konusudur. İfadeleri açıkça göz ardı etmemenin tek amacı, ++i++onları genel olarak yan etkilerle (örneğin a=b[i++]) geçerli ifadelerden ayırmanın tam olarak zor olmasıdır . Bizim için yeterince basit görünebilir, eğer Ejderha Kitabını doğru hatırlıyorsam aslında NP zor bir problem. Bu yüzden bu davranış yasak değil UB'dir.
Konrad Rudolph

1
Performansın geçerli bir argüman olduğuna inanmıyorum. Küçük bir performans düşüşünün fark edilebilir olması için her iki durumda da çok ince fark ve çok hızlı yürütme göz önüne alındığında davanın yeterince yaygın olduğuna inanmaya çalışıyorum - birçok işlemci ve mimaride tanımlamanın etkili bir şekilde ücretsiz olduğunu belirtmiyorum .
DeadMG

9

C # derleyici ekibinin baş tasarımcısı Eric Lippert, blogunda dil özelliği düzeyinde tanımlanmamış bir özellik haline getirmeyi seçmeye yönelik bir dizi husus hakkında bir makale yayınladı . Açıkçası C # farklı bir dildir, farklı faktörler dil tasarımına girer, ancak yaptığı noktalar yine de önemlidir.

Özellikle, mevcut uygulamaları olan ve aynı zamanda bir komitede temsilcileri olan bir dil için mevcut derleyicilere sahip olma konusuna dikkat çekmektedir. Burada durumun bu olup olmadığından emin değilim, ancak çoğu C ve C ++ ile ilgili teknik özellik tartışmasıyla ilgili olma eğilimindedir.

Ayrıca, belirttiğiniz gibi, derleyici optimizasyonu için performans potansiyelidir. Bu günlerde CPU'ların performansının, C gençken olduğundan çok daha fazla büyüklük sırası olduğu doğru olsa da, bu günlerde yapılan büyük miktarda C programlama, özellikle potansiyel performans kazancı ve potansiyel (varsayımsal gelecek) nedeniyle yapılır. ) CPU komut optimizasyonları ve çok çekirdekli işlem optimizasyonları, yan etkileri ve sıralama noktalarını işlemek için aşırı kısıtlayıcı bir kurallar kümesi nedeniyle engellenmek saçma olacaktır.


Bağlantı verdiğiniz makaleden, C # önerdiğimden çok uzak değil gibi görünüyor. Yan etkilerin sıralanması "yan etkilere neden olan iplikten bakıldığında" tanımlanır. Çoklu iş parçacığından bahsetmedim, ancak genel olarak C başka bir iş parçacığında bir gözlemci için çok fazla garanti vermez.
ugoren

5

İlk olarak, tanımlanmamış davranışın tanımına bakalım :

3.4.3

1 tanımlanamayan davranış
davranışı, taşınabilir veya hatalı bir program yapısı veya hatalı verilerin kullanılması üzerine, bu standardın hiçbir gereklilik getirmediği

2 NOT Olası tanımlanmamış davranışlar, durumu öngörülemeyen sonuçlarla tamamen görmezden gelmekten çeviri veya program yürütme sırasında davranmaya kadar uzanır. bir çeviriyi veya yürütmeyi sonlandırmak için (bir tanılama mesajı verilmesi ile) ortamın karakteristik bir özelliği (bir tanılama mesajı verilmesi ile veya bu olmadan).

3 ÖRNEK Tanımlanmamış davranışa bir örnek, tamsayı aşırı akışı üzerindeki davranıştır.

Başka bir deyişle, "tanımsız davranış" basitçe derleyicinin durumu istediği şekilde ele almakta özgür olduğu ve böyle bir eylemin "doğru" olduğu kabul edilir.

Tartışılan sorunun kökü aşağıdaki cümledir:

6.5 İfadeler

...
3 Operatörlerin ve işlenenlerin gruplandırılması sözdizimi ile gösterilir. 74) spesifik fi fonksiyon çağrısı için (daha sonra ed durumlar dışında (), &&, ||, ?:, ve virgül operatörleri), alt ifadelerin değerlendirilmesi sırası ve yan etkiler meydana sırası hem belirtilmezse fi ed .

Vurgu eklendi.

Gibi bir ifade verildi

x = a++ * --b / (c + ++d);

alt ifade a++, --b, cve ++ddeğerlendirilebilir herhangi bir sırada . Ayrıca, yan etkiler a++, --bve ++dsonraki dizi noktadan önce herhangi bir noktada tatbik edilebilir (olsa bile, IOW a++önce değerlendirilir --b, garanti değil aolacak güncellenir önce --bdeğerlendirilir). Diğerlerinin söylediği gibi, bu davranışın gerekçesi, uygulamaya işlemleri en uygun şekilde yeniden düzenleme özgürlüğü vermektir.

Bu nedenle, bunun gibi ifadeler

x = x++
y = i++ * i++
a[i] = i++
*p++ = -*p    // this one bit me just yesterday

vb., farklı uygulamalar için (veya farklı optimizasyon ayarlarıyla aynı uygulama için veya çevre koduna dayalı olarak) farklı sonuçlar verir .

Davranış tanımsız olarak bırakılır, böylece derleyici ne olursa olsun "doğru olanı yapmak" zorunda değildir. Yukarıdaki vakalar yakalamak için yeterince kolaydır, ancak derleme zamanında yakalanması imkansız olan önemsiz sayıda vaka vardır.

Açıkçası, bir dili, değerlendirme sırası ve yan etkilerin uygulanma sırası kesin olarak tanımlanacak ve hem Java hem de C #, büyük ölçüde C ve C ++ tanımlarının yol açtığı sorunlardan kaçınacak şekilde tasarlayabilirsiniz.

Peki, bu değişiklik neden 3 standart revizyondan sonra C'de yapılmadı? Her şeyden önce, 40 yıllık eski C kodu var ve böyle bir değişikliğin bu kodu kırmayacağı garanti edilmiyor. Derleyici yazarlarına biraz yük getirir, çünkü böyle bir değişiklik mevcut tüm derleyicileri derhal uygun hale getirmez; herkes önemli yeniden yazma yapmak zorunda kalacaktı. Ve hızlı, modern CPU'larda bile, değerlendirme sırasını değiştirerek gerçek performans kazanımlarını gerçekleştirmek hala mümkündür.


1
Sorunun çok iyi bir açıklaması. Eski uygulamaları kırma konusunda katılmıyorum - tanımlanmamış / belirtilmemiş davranışın uygulanma şekli bazen standartta herhangi bir değişiklik olmadan derleyici sürümü arasında değişiyor. Tanımlanmış herhangi bir davranışı değiştirmenizi önermiyorum.
ugoren

4

Öncelikle tanımlanmamış olan sadece x = x ++ olmadığını anlamalısınız. Kimse x = x ++ ile ilgilenmez, çünkü onu ne şekilde tanımlasanız da hiçbir anlamı yoktur. Undefined, daha çok "a = b ++, a ve b'nin aynı olduğu yerlerde" - yani

void f(int *a, int *b) {
    *a = (*b)++;
}
int i;
f(&i, &i);

İşlemcinin mimarisi için en verimli olana (ve bunun çevredeki ifadeler için, örneğin örnekten daha karmaşık bir fonksiyon olması durumunda) bağlı olarak, işlevin uygulanabileceği birkaç farklı yol vardır. Örneğin, iki belirgin olan:

load r1 = *b
copy r2 = r1
increment r1
store *b = r1
store *a = r2

veya

load r1 = *b
store *a = r1
increment r1
store *b = r1

Yukarıda listelenen ilk komutun, daha fazla komut ve daha fazla kayıt kullanan kişinin, a ve b'nin farklı olduğu kanıtlanamayan tüm durumlarda kullanılmasını gerektirdiğini unutmayın.


Gerçekten benim önerimin daha fazla makine işlemiyle sonuçlandığı bir durum gösteriyorsun, ama benim için önemsiz görünüyor. Ve derleyicinin hala bir özgürlüğü var - eklediğim tek gerçek gereksinim daha bönce saklamak a.
ugoren

3

miras

Bugün C'nin yeniden keşfedilebileceği varsayımı geçerli olamaz. Üretilen ve günlük olarak kullanılan çok sayıda C kodu satırı var, oyunun ortasında oyunun kurallarını değiştirmek sadece yanlış.

Elbette kurallarınızla C + = diyelim yeni bir dil icat edebilirsiniz . Ama bu C olmayacak.


2
Bugün C'yi yeniden yaratabileceğimizi sanmıyorum. Bu, bu konuları tartışamayacağımız anlamına gelmez. Ancak, önerdiğim şey gerçekten yeniden icat değil. Tanımsız davranışı tanımlı veya belirtilmemiş bir duruma dönüştürmek bir standardı güncellerken yapılabilir ve dil hala C olur.
ugoren

2

Bir şeyin tanımlandığını bildirmek, mevcut derleyicileri tanımınıza göre değiştirmez. Bu özellikle açık veya kapalı bir çok yerde güvenilen bir varsayım söz konusu olduğunda geçerlidir.

Varsayımın ana sorunu değil x = x++;(derleyiciler kolayca kontrol edebilir ve uyarmalıdır), *p1 = (*p2)++ve eşdeğerdir (p1[i] = p2[j]++; ve derleyicinin p1 == p2(C99'da) kolayca bilemediği p1 ve p2 bir işleve parametreler olduğunda). restrictsekans noktaları arasında p1! = p2 varsayım olasılığını yaymak için eklendi, bu nedenle optimizasyon olanaklarının önemli olduğu kabul edildi).


Önerimin bu konuda nasıl bir şey değiştirdiğini görmüyorum p1[i]=p2[j]++. Derleyici herhangi bir diğer ad kabul edemezse, sorun yoktur. Eğer yapamazsa, önce kitap tarafından yapılmalıdır - p2[j]önce artış , p1[i]daha sonra saklayın . Kaybedilen, önemli görünmeyen optimizasyon fırsatları dışında, hiçbir sorun görmüyorum.
ugoren

İkinci paragraf birinciden bağımsız değildi, ancak varsayımın içine girebileceği ve izlenmesi zor olacak yerlere bir örnekti.
AProgrammer

İlk paragraf oldukça açık bir şey ifade eder - derleyiciler yeni bir standarda uymak için değiştirilmelidir. Bunu standartlaştırma ve derleyici yazarlarının takip etmesini sağlama şansım olduğunu düşünmüyorum. Sadece tartışmaya değer olduğunu düşünüyorum.
ugoren

Sorun, dilin ihtiyaç duyduğu herhangi bir değişiklik hakkında derleyicileri değiştirmeye ihtiyaç duymaması değil, değişikliklerin yaygın ve yerinde bulunması zor. En pratik yaklaşım muhtemelen optimize edicinin üzerinde çalıştığı ara formatı değiştirmektir, yani x = x++;yazılmamış gibi ama t = x; x++; x = t;ya x=x; x++;da semantik olarak istediğinizi yapmak (ama teşhis hakkında ne düşünüyorsunuz?). Yeni bir dil için yan etkileri bırakın.
AProgrammer

Derleyici yapısı hakkında çok fazla şey bilmiyorum. Gerçekten tüm derleyicileri değiştirmek isteseydim, daha fazla umursardım. Ama belki x++bir dizi noktası gibi davranmak , sanki bir fonksiyon çağrısı inc_and_return_old(&x)gibi hile yapar gibi.
ugoren

-1

Bazı durumlarda, bu kod tür edildi yeni C ++ 11 Standardında tanımlanan.


5
Ayrıntılı düşünmek ister misiniz?
ugoren

Ben düşünüyorum x = ++xşimdi iyi tanımlanmış (ama değil x = x++)
AA
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.