A +++++ b neden çalışmıyor?


89

Bu kod aşağıdaki hatayı verir:

hata: artış işlenen olarak ldeğer gerekli

Ama baştan sona boşluk koyarsam a++ +ve ++bo zaman iyi çalışır.

İlk örnekteki hata ne anlama geliyor?


3
Bunca zamandan sonra hiç kimsenin sorduğunuz ifadenin C99 ve C11 standardında bir örnek olarak kullanıldığını keşfetmemiş olması şaşırtıcıdır. Aynı zamanda iyi bir açıklama da veriyor. Bunu cevabıma dahil ettim.
Shafik Yaghmour

@ShafikYaghmour - Bu, C11 §6.4 Sözcük Öğeleri in6'daki 'Örnek 2'dir . "Program parçası x+++++y, x ++ ++ + yçözümleme x ++ + ++ ydoğru bir ifade verebilse bile, artırma işleçlerindeki bir kısıtlamayı ihlal eden olarak ayrıştırılır "
Jonathan Leffler

Yanıtlar:


98

printf("%d",a+++++b);(a++)++ + bMaximal Munch Kuralına göre yorumlanır ! .

++(postfix) bir olarak değerlendirilmez lvalueancak işleneninin bir lvalue.

! 6.4 / 4, bir sonraki ön işleme belirtecinin bir ön işleme belirteci oluşturabilecek en uzun karakter dizisi olduğunu söylüyor "


181

Derleyiciler aşamalar halinde yazılır. İlk aşama lexer olarak adlandırılır ve karakterleri sembolik bir yapıya dönüştürür. Böylece "++", birenum SYMBOL_PLUSPLUS . Daha sonra ayrıştırıcı aşaması bunu soyut bir söz dizimi ağacına dönüştürür, ancak sembolleri değiştiremez. Sözcüğü boşluk ekleyerek etkileyebilirsiniz (tırnak içinde olmadıkları sürece bitiş sembolleri).

Normal lexers açgözlüdür (bazı istisnalar dışında), bu nedenle kodunuz şu şekilde yorumlanıyor:

Ayrıştırıcının girdisi bir sembol akışıdır, bu nedenle kodunuz aşağıdaki gibi olacaktır:

Ayrıştırıcının sözdizimsel olarak yanlış olduğunu düşündüğü. (Yorumlara göre DÜZENLE: Anlamsal olarak yanlış çünkü bir r-değerine ++ uygulayamazsınız, bu a ++ sonucunu verir)

dır-dir

Hangisi tamam. Diğer örnekleriniz de öyle.


27
+1 İyi açıklama. Yine de nitelemek zorundayım: Sözdizimsel olarak doğru, sadece bir anlamsal hatası var (sonuçta ortaya çıkan değeri artırma girişimi a++).

7
a++bir rvalue ile sonuçlanır.
Femaref

9
Sözlükler bağlamında, 'açgözlü' algoritmaya genellikle Maximal Munch ( en.wikipedia.org/wiki/Maximal_munch ) adı verilir .
JoeG

14
Güzel. Açgözlü sözcük kullanımı sayesinde birçok dilde benzer tuhaf köşe durumları vardır. İfadeyi daha uzun süre yapmanın onu daha iyi hale getirdiği gerçekten tuhaf bir şey var: VBScript'te x = 10&987&&654&&321yasa dışıdır, ancak tuhaf bir x = 10&987&&654&&&321şekilde yasaldır.
Eric Lippert

1
Açgözlülükle ve tüm bunların düzen ve öncelikle hiçbir ilgisi yoktur. ++, + 'dan daha yüksektir, bu nedenle önce iki ++ yapılacaktır. +++++ b ayrıca + ++ ++ b olacak ve ++ ++ + b değil. Bağlantı için @MByD'ye kredi verin.

30

Lexer, token oluşturmak için genellikle "maksimum munch" algoritması olarak adlandırılan şeyi kullanır. Bu, karakterleri okurken, halihazırda sahip olduğu ile aynı simgenin parçası olamayacak bir şeyle karşılaşana kadar karakterleri okumaya devam ettiği anlamına gelir (örneğin, rakamları okuyorsa, bu yüzden sahip olduğu şey bir sayıdır, eğer karşılaşırsa bir A, sayının parçası olamayacağını bilir. Bu nedenle durur veA sonraki simgenin başlangıcı olarak kullanmak için giriş arabelleğinde ). Daha sonra bu belirteci ayrıştırıcıya geri döndürür.

Bu durumda, bu +++++, lexed anlamına gelir a ++ ++ + b. İlk artım sonrası bir r değeri verdiğinden, ikincisi ona uygulanamaz ve derleyici bir hata verir.

Sadece FWIW, C ++ operator++'da, bunun çalışmasına izin veren bir ldeğer elde etmek için aşırı yükleyebilirsiniz . Örneğin:

Kullanışlı olduğum C ++ derleyicileriyle (VC ++, g ++, Comeau) derler ve çalışır (hiçbir şey yapmaz).


1
"örneğin, rakamları okuyorsa, bu yüzden sahip olduğu şey bir A ile karşılaşırsa, sayının parçası olamayacağını bilir" bir A içeren 16FAmükemmel bir onaltılık sayıdır .
orlp

1
@nightcracker: evet, ancak 0xbaşında bir olmadan , onu tek bir onaltılık sayı değil, 16ardından olduğu gibi ele alacaktır FA.
Jerry Coffin

@ Jerry Coffin: Sayının bir 0xparçası olmadığını söylemedin .
orlp

@nightcracker: hayır, yapmadım - çoğu insanın xbir rakamı dikkate almadığı düşünüldüğünde , oldukça gereksiz görünüyordu.
Jerry Coffin

14

Bu kesin örnek, taslak C99 standardında ( C11'de aynı ayrıntılar ) bölüm 6.4 Sözcüksel öğeler paragraf 4'te şu şekilde anlatılmaktadır:

Girdi akışı, belirli bir karaktere kadar ön işlem belirteçlerine ayrıştırılmışsa, bir sonraki ön işlem simgesi, bir ön işleme belirteci oluşturabilecek en uzun karakter dizisidir. [...]

Sözlük analizinde belirsizliklerden kaçınmak için kullanılan maksimum munch kuralı olarak da bilinen ve geçerli bir simge oluşturmak için olabildiğince çok öğe alarak çalışır.

paragrafta ayrıca iki örnek vardır, ikincisi sizin sorunuz için tam olarak eşleşir ve aşağıdaki gibidir:

ÖRNEK 2 x +++++ y program parçası, x ++ ++ + y olarak ayrıştırılır ve bu, x ++ + ++ y ayrıştırması doğru bir ifade verebilse bile, artırma işleçlerindeki bir kısıtlamayı ihlal eder.

bize şunu söyler:

şu şekilde ayrıştırılacak:

Bu, ilk art artışının sonucu bir değer olduğundan ve artım bir l değeri gerektirdiğinden, artım sonrası kısıtlamaları ihlal eder. Bu, ( vurgu benim ) yazan 6.5.2.4 Postfix artırma ve azaltma operatörleri bölümünde ele alınmıştır :

Sonek artırma veya azaltma operatörünün işleneni, nitelenmiş veya nitelenmemiş gerçek veya imleç tipine sahip olacak ve değiştirilebilir bir ldeğer olacaktır.

ve

Postfix ++ operatörünün sonucu, işlenenin değeridir.

C ++ Gotchas kitabı da Gotcha #17 Maximal Munch Problems'de bu durumu ele alır, C ++ ' da da aynı problemdir ve ayrıca bazı örnekler verir. Aşağıdaki karakter kümesiyle uğraşırken şunu açıklıyor:

sözcük analizcisi şu üç şeyden birini yapabilir:

  • Üç belirteçleri olarak tedavi: -, >ve*
  • Bunu iki simge olarak ele alın: ->ve*
  • Tek bir simge olarak ele alın: ->*

Maksimal Munch kuralı bu belirsizliklerden kaçınmak için izin verir. Yazar buna işaret ediyor ( C ++ bağlamında ):

neden olduğundan çok daha fazla sorunu çözer, ancak iki yaygın durumda bu bir rahatsızlıktır.

İlk örnek, şablon argümanları aynı zamanda şablon olan ( C ++ 11'de çözülen) şablonlardır , örneğin:

Kapanış açısı parantezlerini kaydırma operatörü olarak yorumlayan ve bu nedenle belirsizliği gidermek için bir boşluk gerekli:

İkinci durum, işaretçiler için varsayılan argümanları içerir, örneğin:

*=atama operatörü olarak yorumlanacaktır , bu durumda çözüm, bildirimdeki parametreleri adlandırmaktır.


C ++ 11'in hangi kısmının maksimum çiğneme kuralını söylediğini biliyor musunuz? 2.2.3, 2.5.3 ilginç, ancak C kadar açık değil. >>Kural şu ​​adreste sorulur: stackoverflow.com/questions/15785496/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功bu yanıtı burada görün
Shafik Yaghmour

Güzel teşekkürler, işaret ettiğim bölümlerden biri. Yarın şapkam bittiğinde size oy vereceğim ;-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

12

Derleyiciniz çaresizce ayrıştırmaya çalışıyor a+++++bve bunu olarak yorumluyor (a++)++ +b. Şimdi, artım sonrası ( a++) ' nın sonucu bir ldeğer değildir , yani tekrar sonradan artırılamaz.

Lütfen üretim kalitesi programlarında asla böyle bir kod yazmayın. Kodunuzu yorumlaması gereken zavallı adamı düşünün.


10

a ++ önceki değeri, bir r değeri döndürür. Bunu artıramazsınız.


7

Çünkü tanımlanmamış davranışlara neden olur.

Hangisi o?

Evet, ne siz ne de derleyici bunu bilmiyor.

DÜZENLE:

Gerçek sebep, diğerlerinin söylediği gibi:

Olarak yorumlanır (a++)++ + b.

ancak artım sonrası bir ldeğer gerektirir (adı olan bir değişkendir) ancak (a ++), artırılamayan bir r değeri döndürür ve böylece aldığınız hata mesajına yol açar.

Diğerlerine bunu belirtmek için teşekkürler.


5
a +++ b - (a ++) + b ve a + (++ b) farklı sonuçlara sahip olduğu için aynı şeyi söyleyebilirsiniz.
Michael Chinen

4
Aslında, postfix ++ yüzden, önek ++ daha yüksek önceliğe sahiptir a+++bdaimaa++ + b
MByD

4
Bunun doğru cevap olduğunu sanmıyorum ama yanılıyor olabilirim. Bence lexer onu a++ ++ +bayrıştırılamayacak şekilde tanımlıyor .
Lou Franco

2
Bu cevaba katılmıyorum. 'Tanımlanmamış davranış', belirteçleştirme belirsizliğinden oldukça farklıdır; ve sorunun da olduğunu sanmıyorum.
Jim Blackler

2
Şu anda ... Bence "Aksi takdirde +++++ b ((a ++) ++) + b değerlendirirsiniz" dır a+++++b gelmez için değerlendirmek (a++)++)+b. Kesinlikle GCC ile bu parantezleri ekler ve yeniden oluşturursanız, hata mesajı değişmez.
Jim Blackler

5

Sanırım derleyici bunu

c = ((a ++) ++) + b

++işlenen olarak değiştirilebilecek bir değere sahip olmalıdır. a, değiştirilebilen bir değerdir. a++ancak bir 'rvalue'dur, değiştirilemez.

Bu arada GCC C gördüğüm hata aynıdır, ancak farklı ifadeli: lvalue required as increment operand.


0

Bu öncelik sırasına uyun

1. ++ (ön artış)

2. + - (toplama veya çıkarma)

3. "x" + "y" her iki diziyi de ekleyin

int a = 5,b = 2; printf("%d",a++ + ++b); //a is 5 since it is post increment b is 3 pre increment return 0; //it is 5+3=8

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.