Virgül içeren üçlü operatör neden gerçek durumda yalnızca bir ifadeyi değerlendirir?


119

Şu anda C ++ Primer kitabıyla C ++ öğreniyorum ve kitaptaki alıştırmalardan biri:

Aşağıdaki ifadenin ne yaptığını açıklayın: someValue ? ++x, ++y : --x, --y

Biz ne biliyoruz? Üçlü operatörün virgül operatöründen daha yüksek önceliğe sahip olduğunu biliyoruz. İkili operatörlerde bunu anlamak oldukça kolaydı, ancak üçlü operatörle biraz mücadele ediyorum. İkili operatörler ile "daha yüksek önceliğe sahip" ifadesi, daha yüksek önceliğe sahip ifadenin etrafında parantezler kullanabileceğimiz ve yürütmeyi değiştirmeyeceği anlamına gelir.

Üçlü operatör için şunu yapardım:

(someValue ? ++x, ++y : --x, --y)

etkili bir şekilde aynı kodla sonuçlanır ve bu da derleyicinin kodu nasıl gruplayacağını anlamama yardımcı olmaz.

Bununla birlikte, bir C ++ derleyicisiyle test ettiğimden, ifadenin derlendiğini biliyorum ve bir :operatörün kendi başına neyi temsil edebileceğini bilmiyorum . Böylece derleyici üçlü operatörü doğru yorumluyor gibi görünüyor.

Sonra programı iki şekilde çalıştırdım:

#include <iostream>

int main()
{
    bool someValue = true;
    int x = 10, y = 10;

    someValue ? ++x, ++y : --x, --y;

    std::cout << x << " " << y << std::endl;
    return 0;
}

Sonuçlar:

11 10

Öte yandan, someValue = falsebununla birlikte şunları yazdırır:

9 9

Neden C ++ derleyici, üçlü operatörün gerçek dalı için yalnızca xartarken, üçlü öğenin yanlış dalı için hem xve hem de azaltan kod üretiyor y?

Hatta gerçek dalın etrafına parantez koyacak kadar ileri gittim:

someValue ? (++x, ++y) : --x, --y;

ama yine de sonuçlanır 11 10.


5
"Öncelik" sadece C ++ 'da ortaya çıkan bir fenomendir. Doğrudan dil gramerine bakmak ve ifadelerin nasıl çalıştığını görmek daha basit olabilir.
Kerrek SB

26
Bize ne olduğunu ilkeler hakkında çok. :-) Bunu burada sormanız gerektiği gerçeği, kodun asla diğer programcı arkadaşlarınızın kod incelemesini geçemeyeceğini gösterir. Bu, bunun aslında nasıl çalıştığına dair bilgiyi yararlı olmaktan çok daha az yapar . Tabii ki, Obfuscated C Yarışmasına katılmak istemiyorsanız .
Bo Persson

5
@BoPersson'dan öğrenecek böyle örnekler olmadan, gelecekteki incelemeciler bunu neden üretimden reddetmeleri gerektiğini asla öğrenmeyecekler.
Leushenko

8
@Leushenko - Yine de uyarı çanları çalıyor olmalı. Aynı ifadede birden fazla artış ve azalma (ding, ding, ding!). If-else'i kullanabileceğiniz bir üçlü operatör (ding, ding, ding!). Bekle, bu virgüller korkunç virgül operatörü mü? (ding, DING, DING!) Tüm bu operatörlerde bir öncelikli şey olabilir mi? (ding, ding, ding!) Yani bunu asla kullanamayacağız. Öyleyse, eğer varsa, ne yaptığını bulmak için neden zaman kaybedelim?
Bo Persson

4
Minör nit: adıdır ?olan koşullu operatör . Üçlü operatör terimi, basitçe üç işlenenli bir operatör anlamına gelir. Koşullu operatör, üçlü operatörün bir örneğidir , ancak bir dil (teorik olarak) birden fazla üçlü operatöre sahip olabilir.
bta

Yanıtlar:


122

@Rakete'nin mükemmel yanıtlarında söylediği gibi , bu aldatıcıdır. Buna biraz daha eklemek istiyorum.

Üçlü operatör şu forma sahip olmalıdır:

mantıksal-veya-ifade ? ifade : atama-ifade

Bu yüzden aşağıdaki eşleştirmelerimiz var:

  • someValue: mantıksal veya ifade
  • ++x, ++y: ifade
  • ??? olan atama-ifadesi --x, --y yoksa sadece --x?

Aslında bunun --xnedeni, bir atama ifadesinin virgülle ayrılmış iki ifade olarak ayrıştırılamamasıdır (C ++ dilbilgisi kurallarına göre), bu nedenle --x, --ybir atama ifadesi olarak değerlendirilemez .

Bu, üçlü (koşullu) ifade bölümünün şöyle görünmesine neden olur:

someValue?++x,++y:--x

Okunabilirlik uğruna parantez ++x,++yiçine alınmış gibi hesaplandığını düşünmek yardımcı olabilir (++x,++y); ?ve arasında bulunan her şey :koşulludan sonra sıralanacaktır. (Yazının geri kalanı için onları parantez içine alacağım).

ve şu sırayla değerlendirildi:

  1. someValue?
  2. (++x,++y)veya --x( bool1. sonuca bağlı olarak )

Bu ifade daha sonra virgül operatörünün sol alt ifadesi olarak değerlendirilir, sağ alt ifade --yşu şekilde olur:

(someValue?(++x,++y):--x), --y;

Bu, sol tarafın atılmış bir değer ifadesi olduğu anlamına gelir, yani kesinlikle değerlendirilir, ancak sonra sağ tarafı değerlendirip geri döndürürüz.

Öyleyse ne zaman someValueolur true?

  1. (someValue?(++x,++y):--x)yürütür ve artırır xve yolmak 11ve11
  2. Sol ifade atılır (artışın yan etkileri kalsa da)
  3. : Biz virgül operatörünün sağ tarafını değerlendirmek --y, daha sonra azaltır ygeri10

Davranış "düzeltme" için, grup olabilir --x, --yparantez ile dönüşmesi için birincil ifade olan bir için geçerli bir giriş atama ifade *:

someValue?++x,++y:(--x, --y);

* Bir atama ifadesini birincil ifadeye geri bağlayan oldukça komik uzun bir zincir :

atama-ifade --- (şunlardan oluşabilir) -> koşullu ifade -> mantıksal veya ifade -> mantıksal ve ifade -> kapsayıcı veya ifade -> dışlayıcı veya ifade - -> ve-ifade -> eşitlik-ifade -> ilişkisel-ifade -> kaydırma-ifade -> eklemeli-ifade -> çarpan-ifade -> pm-ifade -> döküm-ifade -> tekli ifade -> sonek ifade -> birincil ifade


10
Dilbilgisi kurallarını çözme zahmetine girdiğiniz için teşekkür ederiz; bunu yapmak, C ++ dilbilgisinin çoğu ders kitabında bulacağınızdan daha fazla olduğunu gösterir.
sdenham

4
@sdenham: İnsanlar "ifade yönelimli dillerin" neden güzel olduğunu sorduklarında (yani, ne zaman { ... }bir ifade olarak ele alınabilir), şimdi bir cevabım var => bu tür hileli şekillerde davranan bir virgül operatörü kullanmak zorunda kalmamak için.
Matthieu M.

assignment-expressionZincir hakkında okumam için bana bir bağlantı verebilir misiniz ?
MiP

@MiP: Onu standardın kendisinden kaldırdım, gram.expr
AndyG

88

Vay canına, bu zor.

Derleyici ifadenizi şu şekilde görür:

(someValue ? (++x, ++y) : --x), --y;

Üçlü operatörün bir : , bu bağlamda kendi başına duramaz, ancak ondan sonra virgülün yanlış duruma ait olması için hiçbir neden yoktur.

Şimdi neden bu çıktıyı aldığınız daha mantıklı olabilir. Eğer someValuedoğruysa, o zaman ++x, ++yve --yetkili bir şekilde değişmez, hangi idam yfakat bir eklerx .

Eğer someValueyanlış, o zaman --xve --yteker ikisini de, azaltarak yürütülür.


42

Neden C ++ derleyicisi, üçlü operatörün gerçek dalı için yalnızca artan bir kod üretir? x

Olanları yanlış yorumladın. Gerçek dal hem xve y. Ancak,y bundan hemen sonra koşulsuz olarak indirilir.

Bu şu şekilde olur: Koşullu operatör C ++ 'da virgül operatöründen daha yüksek önceliğe sahip olduğundan , derleyici ifadeyi şu şekilde ayrıştırır:

   (someValue ? ++x, ++y : --x), (--y);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ^^^^^

--yVirgülden sonra "öksüz" ifadesine dikkat edin. yBaşlangıçta artmış olan azalmaya yol açan şey budur .

Hatta gerçek dalın etrafına parantez koyacak kadar ileri gittim:

someValue ? (++x, ++y) : --x, --y;

Doğru yoldaydınız, ancak yanlış bir dalı parantezize ettiniz: Bunu, diğer dalı parantez içine alarak, şu şekilde düzeltebilirsiniz:

someValue ? ++x, ++y : (--x, --y);

Demo (baskılar 11 11)


5

Senin sorunun, üçlü ifadenin virgülden daha yüksek önceliğe sahip olmamasıdır. Aslında, C ++ basitçe öncelik ile doğru bir şekilde tanımlanamaz - ve tam olarak üç terimli operatör ile virgülün bozulduğu yer arasındaki etkileşimdir.

a ? b++, c++ : d++

şu şekilde ele alınır:

a ? (b++, c++) : d++

(virgül daha yüksek önceliğe sahipmiş gibi davranır). Diğer yandan,

a ? b++ : c++, d++

şu şekilde ele alınır:

(a ? b++ : c++), d++

ve üçlü operatör daha yüksek önceliklidir.


Orta çizgi için yalnızca bir geçerli ayrıştırma olduğundan, bunun hala öncelik alanı içinde olduğunu düşünüyorum, değil mi? Yine de yararlı bir örnek
sudo rm -rf slash

2

Cevaplarda göz ardı edilen bir nokta (yorumlara değinilmiş olsa da), koşullu operatörün değişkene iki değerden birini atamak için bir kısayol olarak gerçek kodda her zaman (tasarım tarafından amaçlanıyor mu?) Kullanılmasıdır.

Yani, daha geniş bağlam şöyle olacaktır:

whatIreallyWanted = someValue ? ++x, ++y : --x, --y;

Yüzünde saçma olan, bu yüzden suçlar çok çeşitlidir:

  • Dil, bir ödevde saçma yan etkilere izin veriyor.
  • Derleyici tuhaf şeyler yaptığınız konusunda sizi uyarmadı.
  • Kitap, 'hileli' sorulara odaklanıyor gibi görünüyor. Sadece arkadaki cevabın "Bu ifadenin yaptığı şey, kimsenin beklemediği yan etkilerin üretilmesi için uydurma bir örnekteki tuhaf uç durumlara dayanmasıdır. Bunu asla yapma."

1
İki değişkenden birinin atanması, üçlü operatörün olağan durumudur, ancak bir ifade biçimine sahip olmanın yararlı olduğu durumlar vardır if(örneğin, bir for döngüsündeki artış ifadesi). Bağlamına de olabilir for (x = 0, y=0; x+y < 100; someValue?(++x, ++y) :( --x, --y))değiştirebilir bir döngü ile xve ybağımsız bir şekilde,.
Martin Bonner Monica'yı

@MartinBonner İkna olmadım ve bu örnek, Tony Hoare'den alıntıladığı gibi, Bo-perrson'ı oldukça iyi gösteriyor.
Taryn
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.