Virgül Operatörü nasıl çalışır?


175

Virgül operatörü C ++ ile nasıl çalışır?

Örneğin, eğer:

a = b, c;  

Bir sonuç b veya c'ye eşit mi?

(Evet, bunun test edilmesi kolay olduğunu biliyorum - birisinin cevabı hızlı bir şekilde bulması için burada belgelemek.)

Güncelleme: Bu soru virgül operatörünü kullanırken bir nüans ortaya koydu. Sadece bunu belgelemek için:

a = b, c;    // a is set to the value of b!

a = (b, c);  // a is set to the value of c!

Bu soru aslında bir kod yazım hatası esinlenerek. Amaçlanan

a = b;
c = d;

Dönüştü

a = b,    //  <-  Note comma typo!
c = d;

Bununla ilgili daha fazla bilgiyi buradan edinebilirsiniz. stackoverflow.com/questions/12824378/…
Mash Kodlama

1
Olası yinelenen `C, virgül operatörü` yapar ne? . Seni bir gün dövdü. Ve lillq'in cevabı, hakkındaki soruya bir cevap veriyor a = (b, c);.
jww

5
Ancak bu durumda a = b, c = d;aslında amaçlananla aynı performansı gösterir a = b; c = d;mi?
Bondolin

@NargothBond Mutlaka değil. Eğer bve dişlev kullanımı (ve değiştirme) değerlendirmeleri genel bir durumu olan, uygulama sırası kadar tanımlanmamıştır C++17.
nyronium

Yanıtlar:



129

Virgül operatörünün C ++ ile aşırı yüklenmiş olabileceğine dikkat edin. Dolayısıyla, gerçek davranış beklenenden çok farklı olabilir.

Örnek olarak, Boost.Spirit sembol tabloları için liste başlatıcıları uygulamak için virgül operatörünü oldukça akıllıca kullanır. Böylece, aşağıdaki sözdizimini mümkün ve anlamlı hale getirir:

keywords = "and", "or", "not", "xor";

Operatör önceliği nedeniyle kodun (kasıtlı olarak!)

(((keywords = "and"), "or"), "not"), "xor";

Yani, çağrılan ilk işleç keywords.operator =("and"), kalan operator,s'lerin çağrıldığı bir proxy nesnesini döndürür :

keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor");

Umm, önceliği değiştiremezsiniz, yani muhtemelen listenin etrafına parantez koymalısınız.
Jeff Burdges

18
@Jeff Aksine. Listenin etrafında bir parantez ile bu işe yaramaz, çünkü derleyici sadece ikisi arasında virgül operatörünü char[]aşırı yüklenemeyen görür . Kod kasıtlı olarak önce operator=ve sonra operator,kalan her eleman için çağırır .
Konrad Rudolph

125

Virgül operatörü, tüm C / C ++ operatörlerinin en düşük önceliğine sahiptir . Bu nedenle, bir ifadeye bağlanan her zaman sonuncudur, yani:

a = b, c;

şuna eşittir:

(a = b), c;

Bir başka ilginç gerçek, virgül operatörünün bir dizi noktası getirmesidir . Bu şu ifade anlamına gelir:

a+b, c(), d

üç alt ifadesinin ( a + b , c () ve d ) sırayla değerlendirilmesi garanti edilir . Yan etkileri varsa bu önemlidir. Normal olarak derleyiciler, uygun gördükleri sırayla alt ifadeleri değerlendirebilirler; örneğin, bir işlev çağrısında:

someFunc(arg1, arg2, arg3)

argümanlar keyfi bir sırayla değerlendirilebilir. İşlev çağrısındaki virgüllerin işleç olmadığını unutmayın ; onlar ayırıcılar.


15
Worth işaret ,böyle düşük önceliğe sahiptir, hatta gerisinde kendisi ;) ... Yani: virgülle yönerge- operatör virgülle yapıldığı haliyle daha düşük bir önceliğe sahiptir ayırıcı . Bu nedenle, tek bir işlev bağımsız değişkeni, değişken ataması veya virgülle ayrılmış diğer bir liste içinde virgül olarak işleç kullanmak istiyorsanız - o zaman parantez kullanmanız gerekir, örneğin:int a = 1, b = 2, weirdVariable = (++a, b), d = 4;
underscore_d

68

Virgül operatörü:

  • en düşük önceliğe sahiptir
  • sol ilişkisel

Tüm türler (yerleşik ve özel) için virgül operatörünün varsayılan bir sürümü tanımlanır ve aşağıdaki gibi çalışır - verilen exprA , exprB:

  • exprA değerlendirilir
  • sonucu exprAyok sayıldı
  • exprB değerlendirilir
  • sonucu exprBtüm ifadenin sonucu olarak döndürülür

Çoğu işleçte, derleyicinin yürütme sırasını seçmesine izin verilir ve nihai sonucu etkilemezse yürütmeyi atlamak bile gerekir (örneğin false && foo()çağrıyı atlar foo). Ancak bu, virgül operatörü için geçerli değildir ve yukarıdaki adımlar her zaman gerçekleşir * .

Uygulamada, varsayılan virgül operatörü noktalı virgülle neredeyse aynı şekilde çalışır. Fark, noktalı virgülle ayrılmış iki ifadenin iki ayrı ifade oluşturması, virgülle ayırmanın tek bir ifade olarak kalmasıdır. Bu nedenle virgül operatörü bazen aşağıdaki senaryolarda kullanılır:

  • C sözdizimi bir ifade değil, tek bir ifade gerektirir . örneğinif( HERE )
  • C sözdizimi tek bir deyim gerektirir, daha fazla değil, örneğin fordöngünün başlatılmasındafor ( HERE ; ; )
  • Kıvırcık parantezleri atlamak ve tek bir ifade tutmak istediğinizde: if (foo) HERE ;(lütfen bunu yapmayın, gerçekten çirkin!)

Bir ifade bir ifade olmadığında, noktalı virgül virgülle değiştirilemez. Örneğin, bunlara izin verilmez:

  • (foo, if (foo) bar)( ifbir ifade değildir)
  • int x, int y (değişken bildirimi bir ifade değildir)

Sizin durumunuzda:

  • a=b, c;, virgül operatörünü aşırı yüklemeyen türde olduğunu a=b; c;varsayarsak a.
  • a = b, c = d;eşdeğer a=b; c=d;, varsayarak avirgül operatörünü aşırı gelmez türde değil.

Her virgülün aslında bir virgül operatörü olmadığını unutmayın. Tamamen farklı bir anlamı olan bazı virgüller:

  • int a, b; --- değişken bildirim listesi virgülle ayrılmıştır, ancak bunlar virgül operatörleri değildir
  • int a=5, b=3; --- bu aynı zamanda virgülle ayrılmış değişken bildirim listesidir
  • foo(x,y)--- virgülle ayrılmış argüman listesi. Aslında xve herhangi bir sırayla ydeğerlendirilebilir !
  • FOO(x,y) --- virgülle ayrılmış makro bağımsız değişken listesi
  • foo<a,b> --- virgülle ayrılmış şablon bağımsız değişken listesi
  • int foo(int a, int b) --- virgülle ayrılmış parametre listesi
  • Foo::Foo() : a(5), b(3) {} --- sınıf yapıcısında virgülle ayrılmış başlatıcı listesi

* Optimizasyon uygularsanız bu tamamen doğru değildir. Derleyici, belirli bir kod parçasının geri kalanını kesinlikle etkilemediğini fark ederse, gereksiz ifadeleri kaldıracaktır.

Daha fazla okuma: http://en.wikipedia.org/wiki/Comma_operator


O eğer fazlalaştı operator ,aşırı yüklü (eğer kısa devre özelliklerini kaybederler gibi sen ilişkilendirilebilirlik üzerinde herhangi bir garanti kaybetmek operator&&ve operator||onlar aşırı yüklü ise)?
YoungJohn

Virgül operatörü, aşırı yüklenip yüklenmediğine bakılmaksızın sol ilişkiseldir. Bir ifade a, b, cher zaman demektir (a, b), cve asla a, (b, c). İkinci yorumlama, elemanlar farklı türdeyse derleme hatasına bile yol açabilir. Argümanların değerlendirme sırası ne olabilir? Bundan emin değilim, ama belki de haklısın: virgül sol çağrışımlı olsa bile daha öncec değerlendirilebilir . (a, b)
CygnusX1

1
Sadece, sipariş bir sınıf oluşturucu virgülle ayrılmış başlatma listesinde hafif açıklama edilir değil listedeki konumuyla belirlenir. Düzen, sınıfın beyan pozisyonuna göre belirlenir. Örneğin daha önce struct Foo { Foo() : a(5), b(3) {} int b; int a; }değerlendirir . Listeniz şöyle ise bu önemlidir: . b 5 olarak ayarlanmaz, aksine derleyicinizin uyarabileceği veya uyarmayabileceği a'nın başlatılmamış değeri. b(3)a(5)Foo() : a(5), b(a) {}
Jonathan Gawrych

Geçenlerde iki şamandıralı bir virgül operatörü ile karşılaştım, bir sayıyı değerlendirmenin ve atmanın anlamı nedir?
Aaron Franke

Kimsenin buna cevap verebileceğini sanmıyorum. Bir bağlamda göstermeniz gerekir. Muhtemelen ayrı bir soru mu?
CygnusX1

38

Değeri aolacaktır b, ancak ifadenin değeri olacaktır c. İçinde

d = (a = b, c);

a eşittir bve deşittir c.


19
Neredeyse doğru. İfadeler değer içermez, ifadeler yapar. Bu ifadenin değeri c'dir.
Leon Timmermans

Bunun yerine neden kullanılıyor a = b; d = c;?
Aaron Franke

Bu, insanların hangi yan etkilerden bahsettiğini anlamamı sağladı.
shoelace

8

b'nin değeri a değerine atanacaktır. C'ye hiçbir şey olmayacak


2

Virgül işleci, atama işlecinden daha düşük önceliğe sahip olduğundan a'nın değeri b'ye eşit olacaktır.


2

Evet Virgül operatörü Atama operatöründen düşük önceliğe sahiptir

#include<stdio.h>
int main()
{
          int i;
          i = (1,2,3);
          printf("i:%d\n",i);
          return 0;
}

Çıktı: i = 3
Çünkü virgül operatörü her zaman en sağdaki değeri döndürür.
Atama İşleci ile virgül operatörü olması durumunda:

 int main()
{
      int i;
      i = 1,2,3;
      printf("i:%d\n",i);
      return 0;
}

Çıkış: i = 1
Bildiğimiz gibi virgül operatörü atamadan daha düşük önceliğe sahiptir .....


Peki ikinci örnek sadece i = 1;bu çizgide olmaktan nasıl farklı ?
Aaron Franke

-3

İlk önce: Virgül aslında bir operatör değil, derleyici için sadece bağlamda anlam kazanan bir jeton diğer jetonlarla .

Bu ne anlama geliyor ve neden rahatsız oluyor?

Örnek 1:

Farklı bir bağlamda aynı belirtecin anlamı arasındaki farkı anlamak için şu örneğe bakıyoruz:

class Example {
   Foo<int, char*> ContentA;
}

Genellikle bir C ++ acemi, bu ifadenin bir şeyleri karşılaştırabileceğini / karşılaştıracağını düşünür, ancak kullanım bağlamına bağlı olarak <, >ve ,simgelerinin anlamı yanlıştır .

Yukarıdaki örneğin doğru yorumlanması elbette bunun bir şablonun bir ürünü olduğudur.

Örnek 2:

Döngünün her yinelemesinden sonra yapılması gereken birden fazla başlatma değişkenine ve / veya birden fazla ifadeye sahip tipik bir döngü için yazdığımızda, virgül de kullanırız:

for(a=5,b=0;a<42;a++,b--)
   ...

Virgülün anlamı kullanım bağlamına bağlıdır, burada foryapı bağlamıdır .

Bağlamdaki virgül aslında ne anlama geliyor?

Daha da karmaşık hale getirmek için (her zaman C ++ 'da olduğu gibi) virgül operatörünün kendisi aşırı yüklenebilir (bunu belirttiği için Konrad Rudolph sayesinde ).

Soruya geri dönmek için, Kurallar

a = b, c;

derleyici için bir şey anlamına gelir

(a = b), c;

çünkü öncelikli bir =belirteci / operatörün önceliği daha yüksek ,simge.

ve bu şu bağlamda yorumlanır:

a = b;
c;

(yorumun bağlama göre değiştiğine dikkat edin, burada bir işlev / yöntem çağrısı veya bir şablon oluşturma işlemi yoktur.)


1
eminim, belki yanlış terminoloji kullandım (lexer için bir jeton, tabii)
Quonux

2
Biri ile çalışır gibi operatör, (sic), virgül gerçekten bir operatörüdür.
DragonLord

2
Verilen virgül belirtecinin virgül operatörü olarak tanınıp tanınmadığını (örneğin, bağımsız değişkenlerin ayırıcısının aksine) tek başına bir sorun olabilirken, bu soru özellikle virgül operatörü ile ilgilidir .
CygnusX1
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.