Po przeczytaniu tej odpowiedzi o niezdefiniowanych zachowaniach 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;
}
Wynik jest 2
. O Boże, nie widziałem nadchodzącego dekretu! Co tu się dzieje?
Ponadto podczas kompilacji powyższego kodu otrzymałem ostrzeżenie o treści:
px.c: 5: 8: ostrzeżenie: lewostronny operand wyrażenia przecinka nie ma żadnego efektu
[-Wunused-value] i = (i, ++i, 1) + 1; ^
Czemu? Ale prawdopodobnie automatycznie odpowie na to odpowiedź na moje pierwsze pytanie.
printf("2\n");
Odpowiedzi:
W wyrażeniu
(i, ++i, 1)
użytym przecinkiem jest operator przecinkaPonieważ odrzuca swój pierwszy operand, jest generalnie użyteczny tylko wtedy, gdy pierwszy operand ma pożądane skutki uboczne . Jeśli efekt uboczny pierwszego operandu nie występuje, kompilator może wygenerować ostrzeżenie o wyrażeniu bez efektu.
Tak więc w powyższym wyrażeniu
i
zostanie obliczony lewy element, a jego wartość zostanie odrzucona. Następnie++i
zostanie oszacowany i zwiększy sięi
o 1 i ponownie wartość wyrażenia++i
zostanie odrzucona, ale efekt ubocznyi
jest trwały . Następnie1
zostanie obliczona i wartość wyrażenia będzie1
.Jest odpowiednikiem
Zauważ, że powyższe wyrażenie jest całkowicie poprawne i nie wywołuje niezdefiniowanego zachowania, ponieważ istnieje punkt sekwencji między oceną lewego i prawego operandu operatora przecinka.
źródło
i
jest inicjowany przez5
. Spójrz na oświadczenie deklaracjiint i = 5;
.++i
, to wyrażenie zostanie ocenione,i
zostanie zwiększone, a ta zwiększona wartość będzie wartością wyrażenia. W przypadkui++
, to wyrażenie zostanie obliczone, stara wartośći
będzie wartością wyrażenia,i
zostanie zwiększona w dowolnym momencie między poprzednim a następnym punktem sekwencji wyrażenia.Cytowanie z
C11
rozdziału6.5.17
, operator przecinkaWięc w twoim przypadku
jest oceniany jako
i
, jest oceniany jako void wyrażenie, wartość odrzucana++i
, jest oceniany jako void wyrażenie, wartość odrzucana1
zwrócona wartość.Tak wygląda końcowe stwierdzenie
i
i
dociera2
. Myślę, że to odpowiada na oba pytania,i
uzyskuje wartość 2?Uwaga: FWIW, ponieważ po ocenie operandu lewej ręki występuje punkt sekwencji , wyrażenie takie jak
(i, ++i, 1)
nie będzie wywoływać UB, jak można ogólnie pomyśleć przez pomyłkę.źródło
i
wyraźnie nie daje efektu! Nie wydaje mi się jednak, żeby było to takie oczywiste dla gościa, który nie zna operatora przecinka (a ja nie wiedziałem, jak szukać pomocy poza zadaniem pytania). Szkoda, że mam tyle głosów przeciw! Sprawdzę pozostałe odpowiedzi i zdecyduję, które zaakceptować. Dzięki! Przy okazji fajna najlepsza odpowiedź.Przeanalizujmy to krok po kroku.
Otrzymujemy więc 2. A teraz ostatnie zadanie:
Cokolwiek znajdowało się w i, zanim zostało nadpisane.
źródło
++i
nie przyczynia się do wyniku.int i = 0; for( ;(++i, i<max); )
Wynik
jest
Dla
ocena ma miejsce w taki sposób, że
,
operator odrzuca oszacowaną wartość i zachowuje tylko najbardziej właściwą wartość, którą jest1
Więc
źródło
Na stronie wiki znajdziesz dobre informacje na temat operatora przecinka .
Zasadniczo to
To znaczy że
z kolei oceni
i
, odrzuci wynik, oszacujei++
, odrzuci wynik, a następnie oceni i zwróci1
.źródło
(void)exp; a= exp2;
podczas gdy tylko potrzebowałema = exp, exp2;
)Musisz wiedzieć, co robi tutaj operator przecinka:
Twoje wyrażenie:
Obliczane jest pierwsze wyrażenie
i
,,, drugie wyrażenie++i
,, i1
zwracane jest trzecie wyrażenie, dla całego wyrażenia.Więc wynik jest:
i = 1 + 1
.Jak widzisz, na Twoje pytanie dodatkowe pierwsze wyrażenie
i
nie ma żadnego efektu, więc kompilator narzeka.źródło
Przecinek ma „odwrotny” priorytet. To właśnie otrzymasz ze starych książek i podręczników C z IBM (lata 70. / 80.). Zatem ostatnie „polecenie” jest używane w wyrażeniu nadrzędnym.
We współczesnym C jego użycie jest dziwne, ale jest bardzo interesujące w starym C (ANSI):
Podczas gdy wszystkie operacje (funkcje) są wywoływane od lewej do prawej, tylko ostatnie wyrażenie zostanie użyte jako wynik warunkowego „while”. Zapobiega to obsłudze „goto” w celu zachowania unikalnego bloku poleceń do wykonania przed sprawdzeniem warunków.
EDYCJA: Pozwala to uniknąć również wywołania funkcji obsługującej, która mogłaby zająć się całą logiką po lewej stronie operandów, a więc zwrócić wynik logiczny. Pamiętaj, że w przeszłości nie mieliśmy funkcji inline w C. Dzięki temu można uniknąć narzutu wywołań.
źródło