La necessità di parentesi nelle macro in C

Ho provato a giocare con la definizione della macro SQR nel seguente codice:

 #define SQR(x) (x*x) int main() { int a, b=3; a = SQR(b+5); // Ideally should be replaced with (3+5*5+3), though not sure. printf("%d\n",a); return 0; } 

Stampa 23 . Se cambio la definizione della macro in SQR(x) ((x)*(x)) l’output è come previsto, 64 . So che una chiamata a una macro in C sostituisce la chiamata con la definizione della macro, ma non riesco ancora a capire come calcola 23 .

Le macro pre-processore eseguono la sostituzione del testo prima che il codice sia compilato, quindi SQR(b+5) traduce in (b + 5 * b + 5) = (6b + 5) = 6 * 3 + 5 = 23

Le chiamate alle funzioni regolari calcolerebbero il valore del parametro (b + 3) prima di passarlo alla funzione, ma poiché una macro è una sostituzione precompilata, l’ordine algebrico delle operazioni diventa molto importante.

Perché (3+5*3+5 == 23) .

Considerando che ((3+5)*(3+5)) == 64 .

Il modo migliore per farlo non è usare una macro :

 inline int SQR(int x) { return x*x; } 

O semplicemente scrivi x*x .

Considera la sostituzione delle macro usando questa macro:

 #define SQR(x) (x*x) 

Usando b+5 come argomento. Fai la sostituzione da solo. Nel tuo codice, SQR(b+5) diventerà: (b+5*b+5) o (3+5*3+5) . Ora ricorda le regole sulla precedenza degli operatori : * prima di + . Quindi questo è valutato come: (3+15+5) , o 23 .

La seconda versione della macro:

 #define SQR(x) ((x) * (x)) 

È corretto, perché stai usando i paren per hide i tuoi argomenti macro dagli effetti della precedenza degli operatori.

Questa pagina che spiega la preferenza dell’operatore per C ha una bella tabella. Ecco la sezione pertinente del documento di riferimento C11.

La cosa da ricordare qui è che dovresti avere l’abitudine di schermare sempre qualsiasi argomento nelle tue macro, usando i parens.

La macro si espande a

  a = b+5*b+5; 

vale a dire

  a = b + (5*b) + 5; 

Quindi 23.

Una macro è solo una sostituzione di testo diretto. Dopo la pre-elaborazione, il tuo codice ha il seguente aspetto:

 int main() { int a, b=3; a = b+5*b+5; printf("%d\n",a); return 0; } 

La moltiplicazione ha una precedenza dell’operatore superiore rispetto all’aggiunta, quindi viene eseguita prima delle due aggiunte quando si calcola il valore per a . L’aggiunta di parentesi alla tua definizione macro corregge il problema rendendolo:

 int main() { int a, b=3; a = (b+5)*(b+5); printf("%d\n",a); return 0; } 

Le operazioni tra parentesi vengono valutate prima della moltiplicazione, quindi le aggiunte vengono eseguite per prime ora e ottieni il risultato a = 64 che ti aspetti.

Dopo la preelaborazione, SQR(b+5) sarà espanso a (b+5*b+5) . Questo ovviamente non è corretto.

Ci sono due errori comuni nella definizione di SQR :

  1. non racchiudere gli argomenti della macro tra parentesi nel corpo della macro, quindi se quegli argomenti sono espressioni, gli operatori con diverse precedenze in quelle espressioni potrebbero causare problemi. Ecco una versione che risolve questo problema

     #define SQR(x) ((x)*(x)) 
  2. valutare gli argomenti della macro più di una volta, quindi se quegli argomenti sono espressioni che hanno effetti collaterali, tali effetti collaterali potrebbero essere presi più di una volta. Ad esempio, considera il risultato di SQR(++x) .

    Usando l’estensione GCC, questo problema può essere risolto in questo modo

     #define SQR(x) ({ typeof (x) _x = (x); _x * _x; }) 

Entrambi questi problemi potrebbero essere risolti sostituendo quella macro con una funzione inline

  inline int SQR(x) { return x * x; } 

Ciò richiede l’estensione in linea GCC o C99, vedere 6.40 Una funzione in linea è veloce come una macro .

Perché le macro sono solo una sostituzione di stringhe e succede prima del processo di completamento. Il compilatore non avrà la possibilità di vedere la variabile Macro e il suo valore. Ad esempio: se una macro è definita come

 #define BAD_SQUARE(x) x * x 

e chiamato così

 BAD_SQUARE(2+1) 

il compilatore vedrà questo

 2 + 1 * 2 + 1 

quale risulterà in, forse, inaspettato risultato di

 5 

Per correggere questo comportamento, dovresti sempre circondare le macro-variabili con parentesi, come ad esempio

 #define GOOD_SQUARE(x) (x) * (x) 

quando viene chiamata questa macro, ad esempio, in questo modo

 GOOD_SQUARE(2+1) 

il compilatore vedrà questo

 (2 + 1) * (2 + 1) 

quale risulterà in

 9 

Inoltre, ecco un esempio completo per illustrare ulteriormente il punto

 #include  #define BAD_SQUARE(x) x * x // In macros alsways srround the variables with parenthesis #define GOOD_SQUARE(x) (x) * (x) int main(int argc, char const *argv[]) { printf("BAD_SQUARE(2) = : %d \n", BAD_SQUARE(2) ); printf("GOOD_SQUARE(2) = : %d \n", GOOD_SQUARE(2) ); printf("BAD_SQUARE(2+1) = : %d ; because the macro will be \ subsituted as 2 + 1 * 2 + 1 \n", BAD_SQUARE(2+1) ); printf("GOOD_SQUARE(2+1) = : %d ; because the macro will be \ subsituted as (2 + 1) * (2 + 1) \n", GOOD_SQUARE(2+1) ); return 0; } 

Basta racchiudere ogni singolo argomento nell’espansione macro tra parentesi.

 #define SQR(x) ((x)*(x)) 

Questo funzionerà per qualunque argomento o valore tu superi.