Linguaggio C - Precedenze degli operatori

Nel C gli operatori hanno una precedenza definita come viene riportato nella tabella seguente in ordine decrescente di priorita'

Precedenze fra Operatori
Operatori Associativita'
() [] -> . -> da sinistra a destra
! ~ ++ -- + - & * (tipo) sizeof <- da destra a sinistra
* / % -> da sinistra a destra
+ - -> da sinistra a destra
<< >> -> da sinistra a destra
< <= > >= -> da sinistra a destra
== != -> da sinistra a destra
& -> da sinistra a destra
^ -> da sinistra a destra
| -> da sinistra a destra
&& -> da sinistra a destra
|| -> da sinistra a destra
?: <- da destra a sinistra
= += -= *= /= %= &= ^= |= <<= >>= <- da destra a sinistra
, -> da sinistra a destra

Ordine di valutazione

Il C non specifica l'ordine di valutazione delle espressioni sulle quali un operatore viene applicato. Per esempio, data l'espressione:
espr_1 op espr_2
il C lascia indefinito se verra' valutata prima espr_1 o prima espr_2.
Questo fatto puo' avere ripercussioni qualora si utilizzino espressioni con effetti collaterali (side effect).
Esempio:
  i=0;  
  c=a[i]+b[++i];        /* c = a[0] + b[1]  OPPURE c = a[1] + b[1] ? */
    
La risposta dell'esempio dipende dalla versione del compilatore!!!
In pratica sono da evitare le espressioni che producano effetti collaterali sulle variabili utilizzate anche in altre parti della medesima espressione.

Associativita' degli operatori

Il linguaggio C definisce, invece, l'ordine in cui vengono associati operatori diversi aventi la stessa priorita'.
Per esempio:
  int *p;
  int b[10];

  p = b;        /* *p equivale a b[0] */
  *p++ = 5;     /* b[0] = 5; *p equivale a b[1] */
  
  p = b;
  *++p = 5;     /* b[1] = 5; *p equivale a b[1] */

  p = b;
  *p = 5;       /* b[0] = 5 */
  b[1] = ++*p;  /* b[1] = 5; *b[0] = 6; *p equivale a b[0] */
    
Nell'esempio appena riportato, sia l'operatore di dereferimento * che l'operatore di incremento ++ (sia che venga impiegato in forma postfissa, che in forma prefissa), hanno la stessa priorita' nelle precedenze degli operatori, ma la loro associativita' va da destra a sinistra.
Cio' comporta gli effetti sotto riportati.
Nel primo caso dell'esempio *p++ viene interpretato come *(p++), pertanto l'operatore incremento (postfisso) e' applicato alla variabile p (puntatore). L'effetto e' quello dell'utilizzo dell'area di memoria puntata dal puntatore p (*p) ed in seguito viene effettuato l'incremento del puntatore (p++).
Differente sarebbe l'effetto (*p)++ che, per forza di cose, non puo' essere scritto senza l'uso delle parentesi. In questo caso, verrebbe incrementato il contenuto (*p) a cui punta p, ma dopo del suo utilizzo a causa dell'operatore di incremento ++ usato in forma postfissa.
Il caso *++p e' interpretato come *(++p), cioe' l'operatore di incremento e' applicato a p in forma prefissa, pertanto prima viene incrementato il puntatore p (++p), e poi (con il valore aggiornato del puntatore) viene effettuato l'accesso all'area di memoria puntata da p incrementato.
Infine ++*p e equivalente a ++(*p), cioe' l'area di memoria puntata da p viene acceduta subito con l'operatore di indirezione *. In seguito, ma prima del suo utilizzo, l'area di memoria viene incrementata dall'operatore di incremento ++ in forma prefissa.

In definitiva, si ha che:
Espressione Equivale a: Significato:
*p++ *(p++) p viene incrementato dopo del suo utilizzo per accedere alla memoria.
*++p *(++p) p viene incrementato prima del suo utilizzo per accedere alla memoria.
++*p ++(*p) p e' utilizzato per l'accesso alla memoria, ma il contenuto della locazione viene incrementato prima del suo utilizzo.
(*p)++ (*p)++ p e' utilizzato per l'accesso alla memoria; il contenuto della locazione viene incrementato dopo del suo utilizzo.

Conclusione: per una maggiore chiarezza (riscontrabile specialmente durante le fasi di manutenzione di un programma) e' consigliabile l'uso delle parentesi al fine di facilitarne la lettura e di evitare di far ricorso a regole sintattiche tipiche del linguaggio.


Indice-C Indice linguaggio C
At Home Umberto Zappi Home Page