Historia pytania
Priorytet operatorów wprowadzono w C w celu zarządzania kolejnością obliczeń w wyrażeniach matematycznych i logicznych. Mimo że operatory matematyczne mają historycznie oczekiwany priorytet (jak w algebrze), pojawienie się wielu nowych operatorów (logicznych, bitowych, przypisania itd.) skomplikowało sytuację. Aby zmniejszyć prawdopodobieństwo błędów i zwiększyć czytelność, powstała oficjalna lista priorytetów i asocjacji operatorów.
Problem
W związku z większą liczbą operatorów i ich różnorodną naturą (arytmetyczne, porównania, przypisania, logiczne, indeksacja) pojawiają się niejednoznaczności przy tworzeniu złożonych wyrażeń. Niewłaściwe zrozumienie kolejności ich stosowania prowadzi do błędów logicznych i nie zawsze oczywistych błędów, szczególnie przy łączeniu operatorów bitowych i logicznych, wskaźników, inkrementacji oraz operatora trójargumentowego.
Rozwiązanie
Przykład kodu:
#include <stdio.h> int main() { int a = 1, b = 2, c = 3, d; d = a + b * c; // b*c wykonuje się wcześniej: d = 1 + (2*3) = 7 printf("%d\n", d); d = a + b << 1; // a + b = 3, następnie 3 << 1 (6) printf("%d\n", d); d = a < b ? a++ : b++; printf("%d\n", d); // a < b prawdziwe => d = a (1), a zwiększy się później }
Kluczowe cechy:
Jaki wynik da wyrażenie x = y > z ? y : z; jeśli zapomnimy nawiasów?
Odpowiedź: Operator trójargumentowy ?: ma niższy priorytet niż >. Najpierw oblicza się (y > z), a następnie wybiera między y a z. Ale jeśli łączyć to z przypisaniem, mogą wystąpić nieoczekiwane efekty. Lepiej zawsze używać nawiasów x = (y > z) ? y : z;.
*Jaki wynik da wyrażenie p++ i dlaczego?
Odpowiedź: Operator postinkrementacji (++) ma wyższy priorytet niż dereferencja (*), więc *p++ staje się *(p++): najpierw p jest używane i zwiększane, dopiero potem dereferencjonowane, co może różnić się od *++p (dereferencja po inkrementacji).
Dlaczego wyrażenie a & b == c nie działa zgodnie z oczekiwaniami?
Odpowiedź: Operator == ma wyższy priorytet niż &, dlatego wyrażenie jest analizowane jako a & (b == c), a nie (a & b) == c, co może prowadzić do nieoczekiwanego wyniku. Aby uzyskać pożądaną logikę, należy użyć nawiasów.
if ((a & b) == c) { ... }
Negatywne przypadki
Podczas optymalizacji kodu programista połączył kilka operatorów bez nawiasów: if( mask & flag == 0 ) ..., w wyniku czego logika sprawdzania działała nieprawidłowo i doprowadziła do awarii systemu.
Zalety:
Wady:
Pozytywne przypadki
Użycie wyraźnej grupowania: if( (mask & flag) == 0 ) ..., logika jest przejrzysta, łatwo zmieniać flagi.
Zalety:
Wady: