C ve C ++ 'da + =' nin sonucu nedir?


93

Aşağıdaki kodu aldım:

#include <stdio.h>
int main(int argc, char **argv) {
    int i = 0;
    (i+=10)+=10;
    printf("i = %d\n", i);
    return 0;
}

Gcc kullanarak bir C kaynağı olarak derlemeye çalışırsam bir hata alıyorum:

error: lvalue required as left operand of assignment

Ama eğer onu g ++ kullanarak bir C ++ kaynağı olarak derlersem hata almam ve çalıştırılabilir dosyayı çalıştırdığımda:

i = 20

Neden farklı davranış?


85
Farklı dil, farklı sözdizimi kuralları ?. Şahsen, kod incelemesinde bu kodu reddederdim.
Maksimum

7
Bu imo gibi kodlardan kaçının ... Herkes için belirsiz.
allaire

1
Şüphesiz, kod temiz değildir ve "gerçek" geliştirmede kaçınılmalıdır. Ama yine de aynı davranışı gözlemliyorum ve bunun nedenlerini bilmek istiyorum.
ulidtko

9
Bu, gerçek bir yazılım parçasından alınan bir kod değildir. Bu sadece kazara karşılaştığım bir gariplik.
Svetlin Mladenov

3
@JohnDibling Bence olumlu oy özellikle (i + = 10) + = 10 hangi dilde meşru kodun olduğunu bilmiyorum ve C ++ 'nın aslında onu derlediğini söylemesinin ilgimi çekiyor.
Tony318

Yanıtlar:


133

Bileşik atama operatörlerinin anlambilgisi C ve C ++ 'da farklıdır:

C99 standardı, 6.5.16, bölüm 3:

Bir atama operatörü, soldaki operand tarafından belirlenen nesnede bir değer depolar. Bir atama ifadesi, atamadan sonra sol işlenenin değerine sahiptir, ancak bir l değeri değildir.

C ++ 5.17.1'de:

Atama operatörü (=) ve bileşik atama operatörlerinin tümü sağdan sola. Hepsi, sol işlenenleri olarak değiştirilebilir bir ldeğer gerektirir ve atama gerçekleştikten sonra sol işlenenin türü ve değeriyle bir değer döndürür.

DÜZENLEME:(i+=10)+=10 C ++ 'daki davranışı C ++ 98'de tanımsızdır, ancak C ++ 11'de iyi tanımlanmıştır. Standartların ilgili bölümleri için NPE'nin sorusuna verilen bu cevaba bakın .


Doğru. Biri sonuç değerini, diğeri ise (adres) değişkenini döndürür
texasbruce

7
Önemli : Bunun (i+=10)+=10C ++ 'da tanımsız bir davranış olduğunu unutmayın , bkz. @Aix yanıtı.
David Rodríguez - dribeas

Şunu demek istemiş @ DavidRodríguez-dribeas belirtilmemiş değil, tanımsız doğru?
Sergey Kalinichenko

4
@dasblinkenlight: Hayır, tanımsız demek istedi . C ++ 03 ve önceki sürümlerde, bir ifadenin ldeğer sonucunu değiştirmek, araya giren sıra noktası olmaması nedeniyle tüm derleyicilerde tahmin edilemez şekilde davranır . Öyle olsaydı belirtilmemiş , bu olur farklı derleyicilerde tahmin edilebileceği ancak farklı davranır .
Justin ᚅᚔᚈᚄᚒᚔ

2
int f(int &y); f(x += 10);Değiştirilmiş değişkene bir referansı bir işleve aktarmak gibi bir ortamda faydalı olurdu .
Phil Miller

51

Geçersiz C kodu olmasının yanı sıra, satır

(i+=10)+=10;

isıra noktaları arasında iki kez değişiklik yapacağından hem C hem de C ++ 03'te tanımsız davranışla sonuçlanır .

Neden C ++ 'da derlenmesine izin verildiğine gelince:

[C ++ N3242 5.17.1] Atama operatörü (=) ve bileşik atama operatörlerinin tümü sağdan sola grup. Tümü, sol işlenenleri olarak değiştirilebilir bir değer gerektirir ve sol işlenenle ilgili olarak bir değer döndürür.

Aynı paragraf şunu söylemeye devam ediyor

Her durumda, atama, sağ ve sol işlenenlerin değer hesaplamasından sonra ve atama ifadesinin değer hesaplamasından önce sıralanır.

Bu, C ++ 11'de ifadenin artık tanımsız davranışa sahip olmadığını gösterir.


3
Sıra noktaları yüzünden kesinlikle UB. Ayrıca C'de geçersiz koddur (ancak C ++ değil) ancak bu, sıra noktalarıyla ilgisi yoktur ve derleyici tarafından yakalanmalıdır.
Konrad Rudolph

2
@KonradRudolph: hayır, derleyici kötü biçimlendirilmiş kodun aksine tanımlanmamış davranışları yakalamak zorunda değildir. "Derleyici tarafından yakalanmalı" kısmı, aynı fikirde olmadığımız yerdir.

2
Sıra noktaları C ++ 11'de mevcut değildir, bu nedenle iUB'nin asıl nedeni , bunlarda sıralanmamış iki değişiklik olmasıdır.
Mankarse

4
Bunun tanımlanmamış bir davranış olduğu yanlış. Atama, atama ifadesinin değer hesaplamasından önceki sıra değilse, i = j+=1belirsiz bir değerle sonuçlanırdı. Aynı paragraftan alıntı yaptığınız "Her durumda, atama, sağ ve sol işlenenlerin değer hesaplamasından sonra ve atama ifadesinin değer hesaplamasından önce sıralanır." Bu nedenle yapmak (i+=10)+=10için iyi tanımlanmıştır i += 10; i += 10;. Öte yandan (i+=10)+=(i+=10)UB.
bames53

2
Yorum yapanlar arasında bu konuda bazı anlaşmazlıklar olduğunu gördüğüm için, bunu ayrı bir soru olarak yayınladım: stackoverflow.com/questions/10655290/…
NPE
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.