Co robi i = (I, ++i, 1) + 1; do?

Po przeczytaniu tej odpowiedzi o niezdefiniowanym zachowaniu i punktach sekwencji napisałem mały program:

#include <stdio.h>

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

Wyjście To 2. O Boże, nie spodziewałem się dekretu! Co tu się dzieje?

Również podczas kompilacji powyższego kodu otrzymałem Ostrzeżenie:

Px.c:5: 8: warning: left-hand operand of comma expression has no effect

  [-Wunused-value]   i = (i, ++i, 1) + 1;
                        ^
Dlaczego? Ale zapewne automatycznie odpowie na nie ODPOWIEDŹ mojego pierwszego pytanie.
Author: Community, 2015-06-03

7 answers

W wyrażeniu (i, ++i, 1) użyty przecinek to operator przecinka

Operator przecinka (reprezentowany przez token ,) jest operatorem binarnym, który ocenia swój pierwszy operand i odrzuca wynik, a następnie ocenia drugi operand i zwraca tę wartość (i typ).

Ponieważ odrzuca swój pierwszy operand, jest on ogólnie użyteczny tylko wtedy, gdy pierwszy operand ma pożądane skutki uboczne. Jeśli efekt uboczny pierwszego operand nie ma miejsca, wtedy kompilator może wygenerować ostrzeżenie o wyrażeniu bez efektu.

W powyższym wyrażeniu wartość i zostanie obliczona i jej wartość zostanie odrzucona. Następnie ++i zostanie ocenione i zwiększy i o 1 i ponownie wartość wyrażenia ++i zostanie odrzucona, , ale efektem ubocznym i jest stałe. Następnie zostanie obliczona wartość 1, a wartością wyrażenia będzie 1.

It jest równoważne

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;  

Zauważ, że powyższe wyrażenie jest całkowicie poprawne i nie wywołuje niezdefiniowanego zachowania , ponieważ istnieje punkt sekwencji pomiędzy wartościami lewego i prawego operandu operatora przecinka.

 255
Author: haccks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-04 07:16:04

Cytat z C11, Rozdział 6.5.17, operator Przecinka

Lewy operand operatora przecinka jest oceniany jako wyrażenie void; jest punkt sekwencji pomiędzy jego oceną A oceną WŁAŚCIWEGO operandu. Wtedy prawo operand jest oceniany; wynik ma swój typ i wartość.

Więc, w Twoim przypadku,

(i, ++i, 1)

Jest oceniana jako

  1. i, zostanie ocenione jako wyrażenie void, wartość odrzucone
  2. ++i, jest oceniana jako wyrażenie void, wartość odrzucona
  3. wreszcie, 1, zwrócona wartość.

Więc ostateczne stwierdzenie wygląda jak

i = 1 + 1;

I i dostaje się do 2. Myślę, że to odpowiada na oba Twoje pytania,

  • Jak i otrzymuje wartość 2?
  • dlaczego jest komunikat ostrzegawczy?

Uwaga: FWIW, ponieważ istnieje punkt sekwencji obecny po ocenie operandu lewej ręki, wyrażenie w stylu (i, ++i, 1) nie wywoła UB, ponieważ jeden Może ogólnie myśleć przez pomyłkę.

 62
Author: Sourav Ghosh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-04 09:42:31
i = (i, ++i, 1) + 1;
Przeanalizujmy to krok po kroku.
(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

Więc otrzymujemy 2. I ostatnie zadanie teraz:

i = 2;

Cokolwiek było w i zanim zostało nadpisane teraz.

 30
Author: dlask,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-03 10:29:28

Wynik

(i, ++i, 1)

Jest

1

Dla

(i,++i,1) 

Ewaluacja odbywa się w taki sposób, że operator , odrzuca ocenianą wartość i zachowa tylko właściwą większość wartości, którą jest 1

Więc

i = 1 + 1 = 2
 18
Author: Gopi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-03 08:19:10

Znajdziesz dobrą lekturę na stronie wiki dla operatora Przecinka .

W Zasadzie to

... ocenia pierwszy operand i odrzuca wynik, a następnie ocenia drugi operand i zwraca tę wartość (i typ).

Oznacza to, że

(i, i++, 1)

Z kolei oceni i, odrzuci wynik, oceni i++, odrzuci wynik, a następnie oceni i zwróci 1.

 14
Author: Tomas Lycken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-03 08:16:24

Musisz wiedzieć, co operator przecinka tutaj robi:

Twoje wyrażenie:

(i, ++i, 1)

Pierwsze wyrażenie, i, jest oceniane, drugie wyrażenie, ++i, jest oceniane, a trzecie wyrażenie, 1, jest zwracane dla całego wyrażenia.

Więc wynik jest: i = 1 + 1.

Na twoje pytanie bonusowe, jak widzisz, pierwsze wyrażenie i nie ma żadnego efektu, więc kompilator narzeka.

 13
Author: songyuanyao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-21 20:35:09

Przecinek ma pierwszeństwo "odwrotne". To jest to, co dostaniesz ze starych książek i podręczników C od IBM (70s/80s). Więc ostatnie 'polecenie' jest tym, co jest używane w wyrażeniu nadrzędnym.

W nowoczesnym C jego użycie jest dziwne, ale jest bardzo interesujące w starym C (ANSI):

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

Podczas gdy wszystkie operacje (funkcje) są wywoływane od lewej do prawej, tylko ostatnie wyrażenie będzie używane jako rezultat warunkowego 'while'. Uniemożliwia to obsługę 'goto', aby zachować unikalny blok poleceń do uruchomienia przed kontrola stanu.

EDIT: to unika również wywołania funkcji obsługującej, która może zająć się całą logiką w lewych operandach i w ten sposób zwrócić wynik logiczny. Pamiętaj, że nie mieliśmy funkcji inline w przeszłości C. Tak więc, może to uniknąć połączenia narzutowego.

 5
Author: Luciano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-17 18:15:35