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:
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();
F1() -> f2 () - > f3 ()
Po ewaluacji funkcji kończysz ewaluację w następujący sposób:
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.(a1 * A2) + A3
a1 + (A2 * A3)
Pytania
- 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?
- 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?
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 .
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
Zauważ również, że Asocjacja nie ma nic wspólnego z porządkiem oceny. In:"Zasady Asocjacja jest podobnie zakodowana w regułach składniowych."
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.
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();
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ść.
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.