Come la precedenza dell’operatore C interrompe il codice (e come risolverlo)

12

Scrivi quello che intendi. Il compilatore legge quello che vuole.

Se vi è mai capitato di fissare una riga di codice C, convinti che stesse facendo una cosa, per poi vederla fare qualcos’altro completamente diverso, avete incontrato la precedenza dell’operatore. Non è un bug. È una caratteristica del linguaggio che morde anche gli sviluppatori esperti.

Prendi questa semplice aritmetica:

x=5+3*6;

La lezione di matematica dice prima di aggiungere. C dice di no. La moltiplicazione e la divisione si collocano su un livello più alto rispetto ad addizione e sottrazione. Il compilatore moltiplica prima 3 per 6. Poi aggiunge 5. X ottiene 23. Non 48. Hai appena perso punti in un quiz. O peggio, un’implementazione di produzione.

La confusione aumenta quando si mescolano gli operatori con la sintassi.

Guarda questa dichiarazione:

char *a[10];

“a” è un puntatore a un array di 10 caratteri? Oppure è un array di 10 puntatori ai caratteri?

Senza conoscere le regole di precedenza specifiche, stai indovinando. In C, le parentesi [] si legano più strettamente dei puntatori *. Quindi questo è un array di 10 puntatori ai caratteri.

Questo è importante perché il C non viene analizzato come l’inglese. Si analizza come una gerarchia rigida.

Puoi scrivere codice che sembra corretto ma fallisce silenziosamente a causa della precedenza.

Considera l’aritmetica dei puntatori mista all’accesso alla struttura.

*p.i = 10;

Questo non funziona come previsto. L’operatore punto . ha una precedenza maggiore rispetto all’operatore di dereferenziazione *. Il compilatore tenta di accedere al membro “i” dell’oggetto puntato da “p” prima di dereferenziare “p”. Ma “p” è un puntatore, non una struttura. Il compilatore soffoca. O peggio, interpreta la memoria in un modo che corrompe i dati.

Devi forzare la questione.

(*p).i = 10;

Vincono le parentesi. Sempre.

La Gerarchia degli Operatori

Kernighan e Ritchie, gli architetti di C, stabilirono la precedenza nel loro testo classico. La tabella seguente mostra l’ordine. La linea superiore è la più alta. Il fondo è il più basso.

  1. Parentesi () e Sottoscrizioni []
  2. Da sinistra a destra.
  3. Operatori unari ++, --, +, -, !, ~, (tipo), * (dereferenza), & (indirizzo), sizeof
  4. Da destra a sinistra.
  5. Moltiplicativo *, /, %
  6. Da sinistra a destra.
  7. Additivo +, -
  8. Da sinistra a destra.
  9. Maiusc <<, >>
  10. Da sinistra a destra.
  11. Relazionale <, <=, >, >=
  12. Da sinistra a destra.
  13. Uguaglianza ==, !=
  14. Da sinistra a destra.
  15. Bit per bit AND &
  16. Da sinistra a destra.
  17. XOR bit a bit ^