Який результат + = в C та C ++?


93

У мене такий код:

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

Якщо я спробую скомпілювати його як джерело C за допомогою gcc, я отримаю повідомлення про помилку:

error: lvalue required as left operand of assignment

Але якщо я скомпілюю його як джерело C ++ за допомогою g ++, я не отримую помилок, і коли я запускаю виконуваний файл:

i = 20

Чому інша поведінка?


85
Інша мова, різні правила синтаксису ?. Особисто я б відхилив цей код під час перегляду коду.
Макс

7
Уникайте подібного коду imo ... Незрозуміло для всіх.
allaire

1
Безсумнівно, код не чистий, і його слід уникати при "реальній" розробці. Але тим не менше, я спостерігаю таку ж поведінку і хотів би знати причини цього.
ulidtko

9
Це НЕ витяг коду з реального програмного забезпечення. Це просто злам, на який я випадково натрапив.
Світлін Младенов

3
@JohnDibling Я думаю, що голосом "за" є саме (i + = 10) + = 10, я не знаю, на якій мові легітимний код, і той факт, що він каже, що С ++ насправді компілює це, мене інтригує.
Tony318

Відповіді:


133

Семантика операторів складеного присвоєння відрізняється в C та C ++:

Стандарт C99, 6.5.16, частина 3:

Оператор присвоєння зберігає значення в об'єкті, позначеному лівим операндом. Вираз присвоєння має значення лівого операнда після присвоєння, але не є значенням l.

У C ++ 5.17.1:

Усі оператори присвоєння (=) та складені оператори присвоєння групуються справа наліво. Всі вони потребують змінного lvalue як свого лівого операнда і повертають lvalue з типом та значенням лівого операнда після того, як відбулося призначення.

РЕДАГУВАТИ: поведінка (i+=10)+=10в C ++ не визначена в C ++ 98, але чітко визначена в C ++ 11. Див. Цю відповідь на запитання NPE щодо відповідних частин стандартів.


Правильно. Один повертає значення результату, а другий - змінну (адресу)
texasbruce

7
Важливо : Зверніть увагу, що (i+=10)+=10невизначена поведінка в C ++, див. Відповідь @aix.
Девід Родрігес - дріба

@ DavidRodríguez-dribeas Ви мали на увазі невизначене , а не невизначене , правда?
dasblinkenlight

4
@dasblinkenlight: Ні, він мав на увазі невизначений . У C ++ 03 і раніше модифікація результату lvalue виразу поводиться непередбачувано у всіх компіляторах через відсутність проміжної точки послідовності. Якби він не був вказаний , він би поводився передбачувано, але по-різному на різних компіляторах .
Джастін ᚅᚔᚈᚄᚒᚔ

2
Це було б корисно в таких параметрах, як int f(int &y); f(x += 10);передача посилання на змінену змінну у функцію.
Філ Міллер,

51

На додаток до недійсного коду С, рядок

(i+=10)+=10;

призведе до невизначеної поведінки як в C, так і в C ++ 03, оскільки вона буде змінюватися iдвічі між точками послідовності.

Щодо того, чому дозволено компілювати в C ++:

[C ++ N3242 5.17.1] Оператор присвоєння (=) та складені оператори присвоєння групуються справа наліво. Всі вони потребують модифікуваного lvalue як свого лівого операнда і повертають lvalue, посилаючись на лівий операнд.

Цей самий абзац продовжує говорити про це

У всіх випадках призначення присвоюється після обчислення значення правого та лівого операндів та перед обчисленням значення виразу присвоєння.

Це свідчить про те, що в C ++ 11 вираз більше не має невизначеної поведінки.


3
Ну, це, безумовно, UB через точки послідовності. Це також неприпустимий код на мові C (але не C ++), але він не пов'язаний з точками послідовності, і його повинен вловлювати компілятор.
Конрад Рудольф

2
@KonradRudolph: ні, компілятор не зобов'язаний вловлювати невизначену поведінку, на відміну від неправильно сформованого коду. Частина "повинен бути схоплений компілятором" - це те, де ми не погоджуємось.

2
Точок послідовності не існує в C ++ 11, тому справжньою причиною UB є те, що є дві модифікації, iякі не є наслідками.
Манкарс

4
Неправда, що це невизначена поведінка. Якби присвоєння не було послідовністю перед обчисленням значення виразу присвоєння, i = j+=1це призвело б до невизначеного значення. З цього ж абзацу ви цитуєте "У всіх випадках призначення присвоюється після обчислення значення правого та лівого операндів та перед обчисленням значення виразу присвоєння." Тому (i+=10)+=10це чітко визначено робити i += 10; i += 10;. З іншого боку (i+=10)+=(i+=10)- UB.
bames53

2
Оскільки я побачив, що між коментаторами існують певні розбіжності з цього приводу, я опублікував це як окреме питання: stackoverflow.com/questions/10655290/…
NPE
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.