İ = (i, ++ i, 1) + 1; yapmak?


174

Tanımlanmamış davranış ve sıralama noktaları hakkındaki bu cevabı okuduktan sonra küçük bir program yazdım:

#include <stdio.h>

int main(void) {
  int i = 5;
  i = (i, ++i, 1) + 1;
  printf("%d\n", i);
  return 0;
}

Çıktı 2. Tanrım, düşüşün geldiğini görmedim! Burada ne oluyor?

Ayrıca, yukarıdaki kodu derlerken, bir uyarı var:

px.c: 5: 8: uyarı: virgül ifadesinin sol tarafındaki işlenmesinin hiçbir etkisi yoktur

  [-Wunused-value]   i = (i, ++i, 1) + 1;
                        ^

Neden? Ama muhtemelen ilk sorumun cevabı tarafından otomatik olarak cevaplanacaktır.


289
Tuhaf şeyler yapma, arkadaşın olmayacak :(
Maroun

9
Uyarı mesajı ilk sorunuzun cevabıdır.
Yu Hao

2
@gsamaras: hayır. elde edilen değer , değişiklik değil atılır. gerçek cevap: virgül operatörü bir dizi noktası oluşturur.
Karoly Horvath

3
@gsamaras Olumlu puanınız olduğunda ve hatta 10'dan fazla soru ile ilgilenmemeniz gerekir.
LyingOnTheSky

9
Not: Optimize edici bir derleyici basit yapabilirprintf("2\n");
chux - Monica

Yanıtlar:


256

İfadede (i, ++i, 1), kullanılan virgül virgül operatörüdür

virgül operatörü (jetonla temsil edilir ,) ilk işlenenini değerlendiren ve sonucu atayan ve daha sonra ikinci işleneni değerlendiren ve bu değeri (ve türü) döndüren ikili bir işleçtir.

İlk işleneni attığı için, genellikle yalnızca ilk işlenenin istenen yan etkilere sahip olduğu durumlarda yararlıdır . İlk işlenenin yan etkisi gerçekleşmezse, derleyici herhangi bir etki olmadan ifade hakkında uyarı üretebilir.

Dolayısıyla, yukarıdaki ifadede, en soldaki ideğer değerlendirilir ve değeri atılır. Daha sonra ++ideğerlendirilecek ve i1 arttırılacak ve ifadenin değeri ++iatılacaktır, ancak yan etkisi ikalıcı olacaktır . Sonra 1değerlendirilecek ve ifadenin değeri değerlendirilecektir 1.

Eşdeğerdir

i;          // Evaluate i and discard its value. This has no effect.
++i;        // Evaluate i and increment it by 1 and discard the value of expression ++i
i = 1 + 1;  

O Not Yukarıdaki anlatım tamamen geçerli olup tanımsız davranış çağırmak değil bir olmadığından dizisi noktası virgül operatörünün sol ve sağ işlenen değerlendirilmesi arasında.


1
son ifade geçerli olmasına rağmen, ikinci ifade ++ i tanımsız bir davranış değil mi? değerlendirilir ve başlatılmamış değişkenin değeri önceden artırılır, hangisi doğru değil? yoksa bir şey mi kaçırıyorum?
Koushik Shetty

2
@Koushik; iile ilklendirilir 5. Beyannameye bakın int i = 5;.
2015'te

1
Ah benim hatam. Üzgünüm dürüstçe görüyorum dint.
Koushik Shetty

Burada bir hata var: ++ i daha sonra değerlendirmek i artacak, i ++ i sonra arttıracak değerlendirecektir.
Quentin Hayot

1
@QuentinHayot; Ne? Herhangi bir yan etki, ifadenin değerlendirilmesinden sonra gerçekleşir. Durumunda ++ibu ifade değerlendirilecektir, iartan ve bu artan değer ifadesinin değeri olacaktır. Durumunda i++, bu sentezleme değerlendirilecektir, eski değerini iifadesinin değeri olur, iönceki ve ifade sonraki sıra noktası arasındaki herhangi bir zamanda artırılır.
2015'te

62

Alıntı C11, bölüm 6.5.17, Virgül operatörü

Virgül operatörünün sol işleneni, geçersiz bir ifade olarak değerlendirilir; değerlendirmesi ile doğru işlenenin değerlendirmesi arasında bir sıralama noktası vardır. Sonra doğru işlenen değerlendirilir; sonucun türü ve değeri vardır.

Yani, sizin durumunuzda,

(i, ++i, 1)

olarak değerlendirilir

  1. i, geçersiz bir ifade olarak değerlendirilir, değer atılır
  2. ++i, geçersiz bir ifade olarak değerlendirilir, değer atılır
  3. son olarak 1, değer geri döndü.

Yani, son ifade şöyle görünüyor:

i = 1 + 1;

ve ialır 2. Sanırım bu her iki soruya da cevap veriyor,

  • i2 değeri nasıl elde edilir?
  • Neden bir uyarı mesajı var?

Not: FWIW, sol el işleneninin değerlendirilmesinden sonra bir dizi noktası bulunduğundan , genellikle yanlışlıkla düşünebileceğinden (i, ++i, 1), böyle bir ifade UB'yi çağırmayacaktır .


+1 Sourav, çünkü bu iaçıkça anlaşılmasının neden hiçbir etkisinin olmadığını açıklıyor ! Ancak, virgül operatörünü bilmeyen bir adam için çok açık olduğunu düşünmüyorum (ve bir soru sormaktan başka nasıl yardım arayacağımı bilmiyordum). Yazık o kadar çok downvotes var! Diğer cevapları kontrol edip hangisini kabul edeceğime karar vereceğim. Teşekkürler! Güzel üst cevap btw.
gsamaras

Neden haccks cevabını kabul ettiğimi açıklamak zorunda olduğumu hissediyorum. Her iki soruma da gerçekten cevap verdiğinden, sizinkini kabul etmeye hazırdım. Ancak, sorumun yorumlarını kontrol ederseniz, bazı insanların ilk bakışta bunun neden UB'yi çağırmadığını göremediğini göreceksiniz. haccks cevapları bazı alakalı bilgiler sağlar. Tabii ki, soruma bağlı UB ile ilgili cevabım var, ancak bazı insanlar bunu özleyebilir. Umarım bana karar vermezsen kararımı kabul etmiş olursun. :)
gsamaras

30
i = (i, ++i, 1) + 1;

Adım adım analiz edelim.

(i,   // is evaluated but ignored, there are other expressions after comma
++i,  // i is updated but the resulting value is ignored too
1)    // this value is finally used
+ 1   // 1 is added to the previous value 1

Böylece 2. ve son ödevi şimdi elde ediyoruz:

i = 2;

Üzerine yazılmadan önce i'de ne varsa.


Bunun virgül operatörü nedeniyle olduğunu belirtmek güzel olurdu. Gerçi adım adım analiz için +1! Güzel üst cevap btw.
gsamaras

Yetersiz açıklama için özür dilerim, orada sadece bir not var ( ... ama görmezden gelin, var ... ). Esas ++iolarak sonucun neden katkıda bulunmadığını açıklamak istedim .
dlask

şimdi benim döngüler her zaman gibi olacakint i = 0; for( ;(++i, i<max); )
CoffeDeveloper

19

Sonucu

(i, ++i, 1)

dır-dir

1

İçin

(i,++i,1) 

değerlendirme, ,operatör değerlendirilen değeri atacak ve en doğru değeri koruyacak şekilde gerçekleşir.1

Yani

i = 1 + 1 = 2

1
Evet ben de düşündüm, ama nedenini bilmiyorum!
gsamaras

@gsamaras çünkü virgül operatörü önceki terimi değerlendirir, ancak atar (yani atama veya benzeri için kullanmaz)
Marco A.

14

Virgül operatörünün wiki sayfasında iyi okumalar bulacaksınız .

Temel olarak,

... ilk işleneni değerlendirir ve sonucu atar, ardından ikinci işleneni değerlendirir ve bu değeri (ve türü) döndürür.

Bunun anlamı şudur ki

(i, i++, 1)

sırayla, isonucu değerlendirir i++, atar , değerlendirir , atar ve sonra değerlendirir ve geri döndürür 1.


O_O cehennem, bu sözdizimi C ++ geçerli, hatırlıyorum ben bu sözdizimi gerekli birkaç yer olduğunu hatırlıyorum (temelde yazdım: (void)exp; a= exp2;ben sadece gerekli iken a = exp, exp2;)
CoffeDeveloper

13

Virgül operatörünün burada ne yaptığını bilmeniz gerekir:

İfadeniz:

(i, ++i, 1)

Birinci ifade, ideğerlendirilir, ikinci ifade ++ideğerlendirilir ve üçüncü ifade 1tüm ifade için döndürülür.

Yani sonucudur: i = 1 + 1.

Bonus sorunuz için, gördüğünüz gibi, ilk ifadenin ihiçbir etkisi yoktur, bu yüzden derleyici şikayet eder.


5

Virgülün 'ters' önceliği vardır. IBM'in eski kitaplarından ve C kılavuzlarından alacaksınız (70s / 80s). Dolayısıyla, son 'komut' üst ifadede kullanılan komuttur.

Modern C'de kullanımı gariptir ancak eski C'de (ANSI) çok ilginçtir:

do { 
    /* bla bla bla, consider conditional flow with several continue's */
} while ( prepAnything(), doSomethingElse(), logic_operation);

Tüm işlemler (işlevler) soldan sağa çağrılırken, koşullu 'while' sonucu olarak yalnızca son ifade kullanılır. Bu, durum kontrolünden önce çalıştırılacak benzersiz bir komut bloğunu tutmak için 'goto'ların işlenmesini önler.

EDIT: Bu aynı zamanda sol işlenenlerde tüm mantık halledebilir ve böylece mantıksal sonuç döndürmek bir işleme fonksiyonu için bir çağrı önlemek. Unutmayın, C'nin geçmişinde satır içi işlevimiz yoktu. Bu, bir çağrı yükünü önleyebilir.


Luciano, şu yanıta da bağlantı var: stackoverflow.com/questions/17902992/… .
gsamaras

Satır içi işlevlerden önceki 90'ların başında, kodu optimize etmek ve düzenli tutmak için çok kullandım.
Luciano
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.