C dilinde + (+ k--) ifadesi


9

Bu soruyu, aşağıdaki kodun çıktısını anlatmamız gereken bir testte gördüm.

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
    k=k++;
    printf("%d\n", k);  
    return 0;
}

Çıktı -1. Yine de bunun cevabı olduğundan emin değilim.

+(+k--)C'de ifade ne anlama geliyor?


4
Bu şekilde biçimlendirildi mi? Bu demek oluyor ;-)
Peter - Yeniden Monica Monica

3
İpucu: Gerçek hayatta asla böyle BS kodu yazmayın.
Jabberwocky

5
Bu UB değil . Cevabımı gör.
14'te dbush

@ Peter-ReinstateMonica Evet, böyle biçimlendirildi.
Ankur Gautam

2
Tuhaf doğası ve tuhaf kıvrımları nedeniyle - k=k++tanımsız, ancak belirsiz bir durum nedeniyle asla yürütülmediği için tanımlanmadı - Bu sorunun bir kopyası olarak kapatmak için oy veriyorum .
Steve Summit

Yanıtlar:


10

[Kayıt için, bu cevabı kabul edildi ve oylandığından beri oldukça önemli bir şekilde düzenledim. Yine de temelde aynı şeyleri söylüyor.]

Bu kod derinden, belki de kasten kafa karıştırıcıdır. Dread undefined davranışının dar şekilde engellenmiş bir örneğini içerir . Bu soruyu oluşturan kişinin çok, çok zeki ya da çok, çok aptal olup olmadığını belirlemek temel olarak imkansızdır. Ve "ders", bu kodun size öğretmeyi ya da test etmeyi iddia edebilir - yani, tekli artı operatörün fazla bir şey yapmaması - kesinlikle bu tür yıkıcı yanlış yönlendirmeyi hak edecek kadar önemli bir kod değildir.

Kodun iki kafa karıştırıcı yönü vardır, garip durum:

while(+(+k--)!=0)

ve kontrol ettiği demans beyanı:

k=k++;

Önce ikinci kısmı ele alacağım.

Böyle bir değişkeni k1 arttırmak istiyorsanız, C size bir değil, iki değil, üç değil, bunu yapmak için dört farklı yol sunar:

  1. k = k + 1
  2. k += 1
  3. ++k
  4. k++

Bu cömertliğe (veya belki de onun yüzünden) rağmen, bazı programcılar kafanız karışır ve

k = k++;

Bunun ne yapması gerektiğini anlayamıyorsanız, endişelenmeyin: kimse yapamaz. Bu ifade, kdeğerini ( k =parça ve k++parça) değiştirmek için iki farklı deneme içerir ve C'de yapılan değişikliklerin hangisinin kazanacağını söyleyen bir kural olmadığından, böyle bir ifade resmen tanımlanmamıştır , yani sadece o gelmiştir hiçbir anlamı tanımlanmış, ancak tüm program şüpheli olduğunu içeren söyledi.

Şimdi, çok dikkatli bakarsanız , bu belirli programda, hattın k = k++gerçekten yürütülmediğini göreceksiniz, çünkü (görmek üzere olduğumuz gibi) kontrol koşulu başlangıçta yanlıştır, bu nedenle döngü 0 kez çalışır . Bu nedenle bu özel program aslında tanımlanmamış olabilir - ancak yine de patolojik olarak kafa karıştırıcıdır.

Ayrıca , bu tür Tanımsız Davranışlarla ilgili tüm sorulara verilen bu standart SO yanıtlarına da bakın .

Ama k=k++parçayı sormadın . İlk kafa karıştırıcı kısmı, +(+k--)!=0durumu sordunuz . Çünkü bu, garip görünüyor olduğunu garip. Hiç kimse, böyle bir kodu gerçek bir programa yazmaz. Dolayısıyla, onu nasıl anlayacağınızı öğrenmek için hiçbir neden yok. (Evet, bu doğru, bir sistemin sınırlarını keşfetmek, onun ince noktaları hakkında bilgi edinmenize yardımcı olabilir, ancak kitabımda, hayali, küfürlü keşiflere karşı yaratıcı, düşündürücü keşifler arasında oldukça açık bir çizgi var ve bu ifade çok açık. bu satırın yanlış tarafı.)

Her neyse, inceleyelim +(+k--)!=0. (Ve bunu yaptıktan sonra, her şeyi unutalım.) Bunun gibi herhangi bir ifade içten dışa doğru anlaşılmalıdır. Sanırım ne olduğunu biliyorsun

k--

yapar. kGeçerli değerini alır ve ifadenin geri kalanına "döndürür" ve az çok eşzamanlı olarak azalır k, yani miktarı k-1tekrar depolar k.

Ama sonra ne yapar +? Bu tek artı, ikili artı değil. Tıpkı sıradan eksi gibi. İkili eksi çıkarma yaparsınız: ifade

a - b

b'yi a'dan çıkarır. Ve biliyorsunuz, tekli eksi bir şeyleri reddediyor: ifade

-a

size bir olumsuzluk verir. Tekin ne +yaptığı ... temelde hiçbir şey. pozitif değerleri pozitif ve negatif değerleri negatif olarak değiştirdikten sonra +asize adeğer verir . Yani ifade

+k--

sana ne verirse verir k--, yani keski değerdir.

Ama işimiz bitmedi, çünkü biz

+(+k--)

Bu sadece +k--size ne +verirse onu alır ve ona yine tek başına uygulanır . Size senkronizasyon Yani ne olursa olsun +k--ne olursa olsun, hangi size verdiği k--hangi kadar verdi, k'nin eski değer.

Sonuçta durum

while(+(+k--)!=0)

çok daha sıradan durumla aynı şeyi yapar

while(k-- != 0)

yapılmalıydı. (Aynı zamanda daha karmaşık görünen durumun while(+(+(+(+k--)))!=0)yapacağı şeyle aynı şeyi yapar . Ve bu parantezler gerçekten gerekli değildir; aynı şey while(+ + + +k--!=0)yaptıklarını da yapar .)

Hatta "normal" durumun ne olduğunu bulmak

while(k-- != 0)

biraz zor. Bu döngüde iki tür şey oluyor: Döngü potansiyel olarak birden çok kez çalıştığından, şunları yapacağız:

  1. daha küçük ve daha küçük k--yapmak için yapmaya devam et k, aynı zamanda
  2. ne yaparsa yap, döngü gövdesini yapmaya devam et.

Ancak k--parçayı, döngüden başka bir yolculuk yapıp yapmayacağınıza karar vermeden önce (veya süreçte) hemen yaparız . Ve unutmayın ki , azaltmadan önce k--eski değerini "döndürür" k. Bu programda, başlangıç ​​değeri k0'dır. Bu nedenle k--, eski 0 değerini "döndürür", ardından k-1 olarak güncellenir . Ama sonra durumun geri kalanı != 0- ama gördüğümüz gibi, durumu ilk kez test ettiğimizde, 0 aldık. Bu yüzden döngüden herhangi bir yolculuk yapmayacağız, bu yüzden sorunlu bir ifade k=k++.

Başka bir deyişle, bu özel döngüde, "devam eden iki şey var" dememe rağmen, 1 şeyin bir kez gerçekleştiği, ancak 2 şeyin sıfır kez gerçekleştiği ortaya çıkıyor.

Her halükarda, umarım bir program için bu kötü bahanenin neden nihai değer olarak -1 baskısı ile sonuçlandığı yeterince açıktır k. Normalde, böyle sınav sorularına cevap vermek istemiyorum - hile gibi geliyor - ama bu durumda, egzersizin tüm noktasına çok titiz bir şekilde katılmıyorum, aldırmıyorum.


1
Onaylanmış örnekler herhangi bir temel sınıfta tipiktir. Muayene sırasında operatörün önceliği hakkında başka bir özlü soru nasıl yazılır ? Matematik öğretiyorum ve eğer bir öğrenci her seferinde bir nikelim olsaydı "Bunu gerçek hayatta ne zaman yapacağım ?" Diye sordu .
Scott

4
@Scott Orada yapmacık, ve sonra orada var yapmacık . Bir sürücü eğitimi eğitmeni olduğunuzu varsayalım. Bir beysbol sopasıyla başını dövdüğünden öğrencinizden yoğun şehir trafiğinden geçmesini istediğini varsayalım. Tartışmalı olarak, öğrenci potansiyel olarak yararlı bir beceri öğrenecektir. Ama buna kötüye kullanım diyorum. Ve bu sorudaki kodun olumsuz pedagojik değeri olan küfürlü olduğunu düşünüyorum.
Steve Summit

1
"Bence duruyorum" bu adil, ama buradaki anahtar kelime "görüş". Bir StackOverflow yanıtı, kişinin görüşlerini ifade etmek için en uygun yer olmayabilir. :)
Scott

1
Kayıt için, bu cevabı kabul edildi ve oylandığından beri oldukça önemli bir şekilde düzenledim. Yine de temelde aynı şeyleri söylüyor.
Steve Summit

11

İlk bakışta, bu kod tanımsız davranışlar çağrıştırıyor gibi görünüyor , ancak durum böyle değil.

Önce kodu doğru şekilde biçimlendirelim:

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
        k=k++;
    printf("%d\n", k);  
    return 0;
}

Şimdi ifadenin k=k++;döngü içinde olduğunu görebiliyoruz .

Şimdi programı izleyelim:

Döngü durum ilk değerlendirildiğinde, ksentezleme 0 değerine sahip k--olan akım değerini k, 0, ve kbir yan etki olarak azaltılır. Bu ifadeden sonra değeri k-1'dir.

Lider +bu ifadesi üzerindeki değer üzerinde hiç bir etkisi, yani var +k--0 değerlendirilmiştir ve benzer şekilde +(+k--)0 olarak değerlendirilir.

Ardından !=operatör değerlendirilir. Beri 0!=0yanlıştır, döngünün gövdesi girilmez . Gövdeye girilmiş olsaydı, bir dizi noktası olmadan k=k++hem okur hem de yazar çünkü tanımlanmamış davranışı çağırırsınız k. Ancak döngü girilmez, bu yüzden UB yok.

Son olarak k-1 değeri yazdırılır.


1
Yürütülmemiş tanımsız davranışların tanımlanıp tanımlanmadığı açık, varoluşsal, felsefi, ölçülemez bir sorudur. Tanımsız değil mi? Ben öyle düşünmüyorum.
Steve Summit

2
@SteveSummit Bu konuda kesinlikle varoluşsal, felsefi, imkansız hiçbir şey yoktur. if (x != NULL) *x = 42;Bu ne zaman tanımsız x == NULL? Tabii ki değil. Kodun yürütülmeyen bölümlerinde tanımlanmamış davranış oluşmaz. Kelime davranışı bir ipucudur. Yürütülmeyen kodun tanımlanmamış veya başka bir davranışı yoktur.
n. 'zamirler' m.

1
@SteveSummit Yürütülmemiş tanımsız davranışlar tüm programı geçersiz kılacaksa, hiçbir C programı davranış tanımlamazdı. Çünkü C programları UB'nin yürütülmesinden her zaman sadece bir döngü / if koşulu. Örnek için basit bir dizi yinelemesi alın: Sınırlı denetim başarısız olana kadar yinelenir. Bir sonraki döngü yinelemesini yürütmek tanımsız bir davranış olurdu, ancak bu asla yürütülmediği için her şey yolunda.
cmaster - eski haline monica

3
Bu konuda tartışmayacağım, çünkü dil avukatı yapmıyorum. Ama bahse girerim bu soruyu sorduysanız ve dil avukatı olarak etiketlediyseniz, heyecanlı bir tartışma başlatabilirsiniz. Bunun k=k++niteliksel olarak farklı olduğunu unutmayın *x=42. İkincisi, xgeçerli bir işaretçi ise iyi tanımlanmıştır , ancak ne olursa olsun birincisi tanımsızdır. (Haklı olabileceğine inanıyorum, ama yine de, onunla ilgili tartışmayacağım ve giderek ustalıkla trollendiğimizden endişe ediyorum.)
Steve Summit

1
"X, geçerli bir işaretçi ise ikincisi iyi tanımlanmıştır, ancak ne olursa olsun birincisi tanımsızdır". Yalnızca tüm programların tanımlanmamış davranışı olabilir. Bu düşünce, tek başına alınan kod parçaları için geçerli değildir.
n. 'zamirler' m.

3

İşte bunun operatör önceliğini gösteren bir sürümü:

+(+(k--))

İki tekli +operatör hiçbir şey yapmaz, bu yüzden bu ifade tam olarak eşdeğerdir k--. Bunu en çok yazan kişi aklını karıştırmaya çalışıyordu.

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.