Jak dokładnie działa operator?: W C?

10

Mam pytanie, w jaki sposób kompilator działa na następującym kodzie:

#include<stdio.h>

int main(void)
{
  int b=12, c=11;
  int d = (b == c++) ? (c+1) : (c-1);
  printf("d = %i\n", d);
}

Nie jestem pewien, dlaczego wynik jest d = 11.

J0S
źródło
1
Dlaczego nie jesteś pewien? Czego jeszcze możesz się spodziewać i dlaczego?
Gerhardh
2
Nie przypominam sobie dokładnej semantyki, ale możesz obserwować nieokreślone zachowanie.
chepner
3
Nie, @chepner, istnieje punkt sekwencyjny po ocenie stanu trójskładnika, przed oceną wybranej alternatywy. To pozwala uniknąć wektora UB, o którym myślę.
John Bollinger
Tak, nie jestem pewien, gdzie myślałem, że kompilator będzie miał wybór.
chepner

Odpowiedzi:

6

W int d = (b == c++) ? (c+1) : (c-1);:

  • Wartością c++jest bieżąca wartość c11. Oddzielnie czwiększa się do 12.
  • b == 11jest fałszywe, ponieważ bma 12 lat.
  • Ponieważ (b == c++)jest fałszywe, (c-1)jest używane. Do ctego punktu należy również wykonać przyrost o 12.
  • Ponieważ cma 12 lat, c-1ma 11 lat.
  • d jest inicjowany do tej wartości, 11.
Eric Postpischil
źródło
5

Zgodnie ze standardem C (operator warunkowy 6.5.15)

4 Pierwszy argument jest oceniany; między oceną a oceną drugiego lub trzeciego operandu występuje punkt sekwencyjny (w zależności od tego, który jest oceniany). Drugi operand jest oceniany tylko wtedy, gdy pierwszy porównuje nierówny z 0; trzeci argument jest oceniany tylko wtedy, gdy pierwszy porównuje równy 0; wynikiem jest wartość drugiego lub trzeciego operandu (zależnie od tego, który jest oceniany), przekonwertowany na typ opisany poniżej. 110)

Tak więc w inicjującym wyrażeniu tej deklaracji

int d = (b == c++) ? (c+1) : (c-1);

zmienna bjest porównywana z wartością zmiennej, cponieważ operator po inkrementacji zwraca wartość swojego argumentu przed jego inkrementacją.

Ponieważ wartości nie są sobie równe ( bjest ustawione na 12, podczas gdy cjest ustawione na 11), wówczas podwyrażenie (c-1)jest oceniane.

Zgodnie z cytatem istnieje punkt sekwencyjny po ocenie stanu operatora. Oznacza to, że po ocenie warunku cma wartość 12po zastosowaniu operatora po-przyrostowego do zmiennej c. W rezultacie zmienna d jest inicjalizowana przez wartość 1( 12 - 1).

Vlad z Moskwy
źródło
2
Jedyna poprawna odpowiedź - na ten konkretny przypadek należy odpowiedzieć, podając punkt sekwencji w ?:. Ponieważ zwykle w C łączenie ++z innymi operacjami na tym samym operandzie jest niezdefiniowanym zachowaniem. Ten kod działa tylko przewidywalnie, ponieważ ?:ma różne specjalne reguły dotyczące płatków śniegu.
Lundin
4

Ponieważ warunek jest fałszywy, dlatego falsetak się stanie :, c-1ale ponieważ zwiększyłeś cwarunek o c++, dlatego cjest teraz 12. Wynik zatem 12 - 1, który wynosi 11.

EDYCJA: To, co OP źle zrozumiał, to przyrost postu.

Tak więc to, co się naprawdę wydarzyło, wygląda następująco:

#include<stdio.h>
int main(void)
{
  int b=12, c=11;
  int d;

  if (b == c) { // 12 == 11 ? -> false
    c = c + 1;
    d = c + 1;
  } else { // this executes since condition is false
    c = c + 1; // post increment -> c++ -> c = 12 now
    d = c - 1; // 12 - 1 = 11 -> d = 11
  }
  printf("d = %i\n", d);
}
Eraklon
źródło
1
Myślę, że PO odnosi się do kolejności operacji, biorąc c++pod uwagę warunek. Warunek jest fałszywy, ale do obliczenia używana jest wartość oryginalna parametru , a nie wersja przyrostowa. cc - 1
chepner
1
Myślałem, że od 12 == 11 + 1 to prawda ...
J0S
Ale to nieprawda, ponieważ nowa wartość c została użyta, czy też brakuje mi twojego punktu?
Eraklon
Myślę, że może być nieporozumienie pomiędzy c++i++c
ChatterOne
@ N00b c++jest operatorem post- increment. Wartość c++wynosi 11, z efektem ubocznym tworzenia c == 12. ++cmiałby wartość 12.
chepner
4

Przetłumaczony na zwykłą instrukcję if twój kod wyglądałby tak:

int b=12, c=11;
int d;

if (b == c++)
   d = c+1;
else
   d = c-1;

Chodzi tutaj o to, że c jest zwiększane po sprawdzeniu warunku. Wchodzisz więc w elsestan, ale c ma już tam wartość 12.

Odyseusz
źródło
1

Patrz operator trójskładnikowy.

Składnia

stan: schorzenie ? wartość_jeśli_prawda: wartość_jeśli_fałsz

Więc napisałeś

int d = (b == c++) ? (c+1) : (c-1);

W tej sytuacji wynikiem będzie 11, ponieważ po sprawdzeniu wartość „c” wzrasta (c + 1 = 12), a dopiero potem ustawia wartość „d” na c (12) -1, która wynosi 11.

Jeśli używałeś na przykład:

int d = (b == ++c) ? (c+1) : (c-1);

Wartość „c” byłaby zwiększona przed sprawdzeniem instrukcji, więc byłaby to prawda, a wartość „d” wynosiłaby c (12) +1, czyli 13.

Neto Costa
źródło