Що значить i = (i, ++ i, 1) + 1; робити?


174

Прочитавши цю відповідь про невизначене поведінку та пункти послідовності, я написав невеличку програму:

#include <stdio.h>

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

Вихід є 2. О Боже, я не бачив, як настав прихід! Що тут відбувається?

Також під час компілювання вищевказаного коду я отримав попередження:

px.c: 5: 8: попередження: лівий операнд вираження комами не впливає

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

Чому? Але, ймовірно, він автоматично відповість на відповідь мого першого запитання.


289
Не робіть дивних речей, у вас не буде друзів :(
— Maroun

9
Попереджувальне повідомлення - це відповідь на ваше перше запитання.
— Ю-Хао

2
@gsamaras: ні. отримане значення відкидається, а не модифікація. реальна відповідь: оператор з комами створює точку послідовності.
— Каролі Горват

3
@gsamaras Ви не повинні турбуватися, коли у вас є позитивний бал, а ще більше 10 запитань.
— LyingOnTheSky

9
Примітка: Оптимізуючий компілятор може зробити простоprintf("2\n");
— chux - Поновіть Моніку

Відповіді:


256

У виразі (i, ++i, 1)використовувана кома є оператором коми

оператор комами (представлений маркером ,) - це двійковий оператор, який оцінює свій перший операнд і відкидає результат, а потім оцінює другий операнд і повертає це значення (і тип).

Оскільки він відкидає свій перший операнд, він, як правило, корисний лише тоді, коли перший операнд має бажані побічні ефекти . Якщо побічний ефект для першого операнда не має місце, то компілятор може генерувати попередження про вираз без ефекту.

Отже, у вищенаведеному виразі iбуде оцінено крайнє ліве місце , а його значення буде відкинуто. Тоді ++iбуде оцінено і збільшиться iна 1 і знову значення виразу ++iбуде відкинуто, але побічний ефект до iпостійного . Тоді 1буде оцінено і значення виразу буде 1.

Це еквівалентно

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;  

Зауважте, що вищенаведений вираз є абсолютно дійсним і не викликає невизначеної поведінки, оскільки між оцінкою лівого та правого операндів оператора комами є точка послідовності .


1
хоча остаточне вираження є дійсним, чи не другий вираз ++ i невизначена поведінка? вона оцінюється і значення неініціалізованої змінної попередньо збільшується, що не так? чи я щось пропускаю?
— Кушик Шетті

2
@Koushik; iініціалізовано з 5. Подивіться на заяву декларації int i = 5;.
— hackcks

1
ой мій поганий. Вибачте, я чесно бачу це бачити.
— Кушик Шетті

Тут є помилка: ++ я збільшить, а потім оцінюю її, тоді як i ++ оцінить, а потім збільшить її.
— Квентін Гайот

1
@QuentinHayot; Що? Будь-які побічні ефекти мають місце після оцінки вираженості. У випадку ++iцього вираження буде оцінено, iбуде збільшено, і це збільшене значення буде значенням виразу. У випадку i++цього вираження буде оцінено, старе значення iбуде значенням виразу, iзбільшується в будь-який час між попередньою та наступною точкою послідовності виразу.
— хаки

62

Цитуючи з C11, глава 6.5.17, оператор Comma

Лівий операнд оператора комами оцінюється як недійсний вираз; існує точка послідовності між її оцінкою та правою операндом. Потім оцінюється правий операнд; результат має свій тип та значення.

Отже, у вашому випадку

(i, ++i, 1)

оцінюється як

  1. i, оцінюється як недійсний вираз, значення, яке відкидається
  2. ++i, оцінюється як недійсний вираз, значення, яке відкидається
  3. нарешті, 1значення повернулося.

Отже, виглядає остаточне твердження

i = 1 + 1;

і iдістається 2. Я думаю, це відповідає на обидва ваші запитання,

  • Як iотримується значення 2?
  • Чому виникає попереджувальне повідомлення?

Примітка: FWIW, оскільки після оцінки лівого операнда присутній пункт послідовності , такий вираз, як (i, ++i, 1)не викликає UB, як можна вважати, помилково.


+1 Сурав, оскільки це пояснює, чому інтіалізація iявно не має ефекту! Однак я не думаю, що це було настільки очевидним для хлопця, який не знає оператора комами (і я не знав, як шукати допомогу, крім запитання). Шкода, що я отримав стільки знижок! Я перевірю інші відповіді, а потім вирішу, яку прийняти. Дякую! Приємна відповідь btw.
— gsamaras

Я відчуваю, що мушу пояснити, чому я прийняв відповідь на хаки. Я був готовий прийняти ваше, оскільки це справді відповідає на мої обидва питання. Однак якщо ви перевірите коментарі до мого питання, ви побачите, що деякі люди не можуть побачити з першого погляду, чому це не викликає UB. відповіді на хаки містять релевантну інформацію. Звичайно, я маю відповідь щодо UB, пов'язану в моєму питанні, але деякі люди можуть це пропустити. Сподіваюся, ви згодні з моїм рішенням, якщо не дасте мені знати. :)
— gsamaras

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

Давайте проаналізуємо це поетапно.

(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

Таким чином ми отримуємо 2. І остаточне завдання зараз:

i = 2;

Що б не було в i до того, як зараз його перезаписати.


Було б непогано констатувати, що це відбувається через оператор коми. Хоча +1 за покроковий аналіз! Приємна відповідь btw.
— gsamaras

Вибачте за недостатнє пояснення, у мене є лише записка ( ... але проігноровано, є ... ). Я хотів пояснити, головним чином, чому ++iце не сприяє результату.
— тьмяний

тепер мій для циклів завжди буде якint i = 0; for( ;(++i, i<max); )
— CoffeDeveloper

19

Підсумок

(i, ++i, 1)

є

1

Для

(i,++i,1) 

оцінка буває такою, що ,оператор відкидає оцінене значення і збереже саме найвище значення, яке є1

Так

i = 1 + 1 = 2

1
Так, я теж про це думав, але не знаю чому!
— gsamaras

@gsamaras тому, що оператор кома оцінює попередній термін, але відкидає його (тобто не використовує його для виконання завдань тощо)
— Марко А.

14

Ви знайдете хороше прочитання на сторінці вікі для оператора Comma .

В основному, це

... оцінює свій перший операнд і відкидає результат, а потім оцінює другий операнд і повертає це значення (і тип).

Це означає що

(i, i++, 1)

буде, у свою чергу, оцінювати i, відкидати результат, оцінювати i++, відкидати результат, а потім оцінювати та повертати 1.


O_O пекло, чи синтаксис дійсний на C ++, я пам’ятаю, що в мене було небагато місць, де мені знадобився цей синтаксис (в основному я писав: (void)exp; a= exp2;поки мені просто потрібно a = exp, exp2;)
— CoffeDeveloper

13

Вам потрібно знати, що тут робить оператор комами:

Ваше вираження:

(i, ++i, 1)

Перший вираз i,, оцінюється, другий вираз, ++iоцінюється, а третій вираз, 1повертається для всього виразу.

Таким чином , результат: i = 1 + 1.

На ваше бонусне питання, як бачите, перший вираз iвзагалі не має ефекту, тому компілятор скаржиться.


5

Кома має "зворотний" пріоритет. Це ви отримаєте зі старих книг та посібників з C від IBM (70-ті / 80-ті роки). Отже, остання "команда" - це те, що використовується в батьківському виразі.

У сучасному C його використання дивне, але дуже цікаве для старих C (ANSI):

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

Хоча всі операції (функції) викликаються зліва направо, в результаті буде використано лише останнє вираження до умовного 'while'. Це запобігає обробці "goto" для збереження унікального блоку команд для запуску перед перевіркою умови.

EDIT: Це дозволяє також уникнути виклику функції обробки, яка могла б піклуватися про всю логіку в лівих операндах і таким чином повертати логічний результат. Пам'ятайте, що ми не виконували вбудованої функції в минулому C. Отже, це може уникнути накладних викликів.


Лучано, ви також отримали посилання на цю відповідь: stackoverflow.com/questions/17902992/… .
— gsamaras

На початку 90-х років до вбудованих функцій я багато використовував її для оптимізації та організації коду.
— Лучано
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.