Kto definiuje pierwszeństwo operatora C i asocjację?

Wprowadzenie

W każdym podręczniku do C / C++ znajdziesz tabelę precedencji operatorów i asocjacji, taką jak:

Tabela Precedencji Operatorów I Asocjacji

Http://en.cppreference.com/w/cpp/language/operator_precedence

Jedno z pytań na temat StackOverflow zadało coś takiego:

W jakiej kolejności wykonują następujące funkcje:

f1() * f2() + f3();
f1() + f2() * f3();

[[4]] nawiązując do poprzedniego wykresu z ufnością odpowiedziałem, że funkcje mają asocjację od lewej do prawej, więc w poprzednich instrukcjach są oceniane tak w obu przypadkach:

F1() -> f2 () - > f3 ()

Po ewaluacji funkcji kończysz ewaluację w następujący sposób:

(a1 * A2) + A3
a1 + (A2 * A3)

Ku mojemu zdziwieniu, Wiele osób powiedziało mi, że się myliłem. Zdeterminowany, aby udowodnić im, że się mylą, postanowiłem przejść do standardu ANSI C11. Po raz kolejny zaskoczył mnie fakt, że o tym bardzo niewiele mówi się na temat precedencji operatorów i asocjacji.

Pytania

  1. jeśli moje przekonanie, że funkcje są zawsze oceniane od lewej do prawej jest błędne, co naprawdę oznacza tabela odnosząca się do pierwszeństwa funkcji i asocjacji?
  2. Kto definiuje precedens operatora i asocjację, jeśli nie jest to ANSI? Jeśli to ANSI tworzy definicję, to dlaczego niewiele mówi się o pierwszeństwie operatora i asocjacji? Jest operatorem nadrzędnym i Asocjacja wywnioskowana ze standardu ANSI C czy jest zdefiniowana w matematyce?
Author: Zero Piraeus, 2013-12-24

5 answers

Pierwszeństwo operatora jest określone w odpowiedniej normie. Standardy C i C++ są jedyną prawdziwą definicją tego, czym dokładnie są C i C++. Więc jeśli przyjrzysz się uważnie, szczegóły są tam. W rzeczywistości szczegóły są w gramatyce języka. Na przykład, spójrz na regułę produkcji gramatycznej dla + i - W C++ (łącznie addytywne wyrażenia):

additive-expression:
  multiplicative-expression
  additive-expression + multiplicative-expression
  additive-expression - multiplicative-expression

Jak widać, multiplikatywne wyrażenie jest podzbiorem addytywne wyrażenie . Oznacza to, że jeśli masz coś w rodzaju x + y * z, wyrażenie y * z jest podwyrażeniem x + y * z. Definiuje to pierwszeństwo pomiędzy tymi dwoma operatorami.

Widzimy również, że lewy operand addytywne wyrażenie rozszerza się do innego addytywne wyrażenie , co oznacza, że z x + y + z, x + y jest jej subekspresją. To definiuje asocjację .

Asocjacja określa sposób przylegania zastosowania tego samego operatora zostaną pogrupowane. Na przykład, + jest asocjacyjny od lewej do prawej, co oznacza, że x + y + z będą grupowane w następujący sposób: (x + y) + z.

Nie myl tego z kolejnością oceny . Nie ma absolutnie żadnego powodu, dla którego wartość z nie może być obliczona przed x + y jest. Liczy się to, że to x + y jest obliczane, a nie y + z.

Dla operatora wywołania funkcji, Asocjacja od lewej do prawej oznacza, że f()() (co może się zdarzyć, jeśli f zwracany wskaźnik funkcji, na przykład) jest zgrupowany w następujący sposób: (f())() (oczywiście drugi kierunek nie miałby sensu).

Teraz rozważmy przykład, na który patrzyłeś:

f1() + f2() * f3()

Operator * ma wyższy priorytet niż operator +, więc wyrażenia są grupowane tak:

f1() + (f2() * f3())

Nie musimy nawet brać pod uwagę asocjacji, ponieważ nie mamy żadnego z tych samych operatorów sąsiadujących ze sobą.

Ocena wyrażenia wywołania funkcji są jednak całkowicie niezrównoważone. Nie ma powodu, dla którego f3 nie można było najpierw zadzwonić, potem f1, a potem f2. Jedynym wymogiem w tym przypadku jest to, że operandy operatora są oceniane przed operatorem is. Oznacza to, że f2 i f3 muszą być wywołane przed * jest oceniana i * musi być oceniana i f1 musi być wywołana przed + jest oceniana.

Niektórzy operatorzy narzucają jednak sekwencjonowanie ocena ich operandów. Na przykład w x || y, x jest zawsze oceniana przed y. Pozwala to na zwarcie, gdzie y nie trzeba oceniać, jeśli x jest już znane jako true.

Kolejność oceny została wcześniej zdefiniowana w C i c++ przy użyciu punktów sekwencji , i oba zmieniły terminologię, aby zdefiniować rzeczy w kategoriach uporządkowane przed relacji. Aby uzyskać więcej informacji, zobacz Undefined Behaviour and Punkty Sekwencji .

 44
Author: Joseph Mansfield,
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-05-23 11:45:43

Pierwszeństwo operatorów w standardzie C wskazuje składnia.

(C99, 6.5P3) "grupowanie operatorów i operandów jest wskazane przez składnię. 74)"

74) "składnia określa pierwszeństwo operatorów w ocenie wyrażenia"

C99]}

" reguły pierwszeństwa są zakodowane w regułach składniowych dla każdego operatora."

I

"Zasady Asocjacja jest podobnie zakodowana w regułach składniowych."

Zauważ również, że Asocjacja nie ma nic wspólnego z porządkiem oceny. In:

F1 () * f2 () + f3 ()

Wywołania funkcji są oceniane W dowolnej kolejności. Reguły składniowe C mówią, że f1() * f2() + f3() oznacza (f1() * f2()) + f3(), ale kolejność ewaluacji operandów w wyrażeniu jest nieokreślona.

 10
Author: ouah,
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
2013-12-24 23:32:31

Jednym ze sposobów myślenia o pierwszeństwie i asocjacji jest wyobrażenie sobie, że język dopuszcza tylko wyrażenia zawierające przypisanie i jeden operator, a nie wiele operatorów. Więc stwierdzenie takie jak:

a = f1() * f2() + f3();

Nie byłoby dozwolone, ponieważ ma 5 operatorów: 3 wywołania funkcji, mnożenie i dodawanie. W tym uproszczonym języku trzeba by przypisać wszystko tymczasowym, a następnie połączyć je:

temp1 = f1();
temp2 = f2();
temp3 = temp1 * temp2;
temp4 = f3();
a = temp3 + temp4;

Asocjacja i pierwszeństwo określają, że ostatni w tej kolejności należy wykonać dwa twierdzenia, ponieważ mnożenie ma wyższy priorytet niż dodawanie. Ale nie określa względnej kolejności pierwszych 3 stwierdzeń; byłoby równie ważne, aby zrobić:

temp4 = f3();
temp2 = f2();
temp1 = f1();
temp3 = temp1 * temp2;
a = temp3 + temp4;

Sftrabbit podał przykład, w którym znaczenie ma Asocjacja operatorów wywołania funkcji:

a = f()();

Upraszczając to jak wyżej, staje się to:

temp = f();
a = temp();
 8
Author: Barmar,
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
2013-12-24 23:54:40

Pierwszeństwo i Asocjacja są zdefiniowane w standardzie i decydują o tym, jak zbudować drzewo składni. Pierwszeństwo działa według typu operatora (1+2*3 jest 1+(2*3), a nie (1+2)*3), a Asocjacja działa według pozycji operatora (1+2+3 jest (1+2)+3, a nie 1+(2+3)).

Kolejność ewaluacji jest inna - nie definiuje jak zbudować drzewo składni - definiuje jak evaluate węzły operatorów w drzewie składni. Kolejność oceny jest zdefiniowana, a nie określona - nigdy nie można na niej polegać, ponieważ Kompilatory mogą wybrać dowolne zamówienie, które uznają za stosowne. Dzieje się tak, aby kompilatorzy mogli próbować zoptymalizować kod. Chodzi o to, że programiści piszą kod, na który nie powinna mieć wpływu kolejność ewaluacji, i dają te same wyniki bez względu na kolejność.

 4
Author: Idan Arye,
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
2013-12-24 23:40:36

Asocjacja od lewej do prawej oznacza, że f() - g() - h() oznacza (f() - g()) - h(), nic więcej. Załóżmy, że f zwraca 1. Załóżmy, że g zwraca 2. Załóżmy, że h zwraca 3. Asocjacja od lewej do prawej oznacza, że wynikiem jest (1 - 2) - 3 lub -4: kompilator nadal może najpierw wywołać g i h, co nie ma nic wspólnego z asocjacją, ale nie może dać wyniku 1 - (2 - 3), który byłby czymś zupełnie innym.

 3
Author: ,
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
2013-12-24 23:38:40