Domanda

In una precedente question quella che pensavo fosse una buona risposta è stata votata per l'uso suggerito di macro

#define radian2degree(a) (a * 57.295779513082)
#define degree2radian(a) (a * 0.017453292519)

invece di funzioni incorporate. Scusa la domanda per i principianti, ma cosa c'è di così male nelle macro in questo caso?

È stato utile?

Soluzione

Ci sono un paio di cose strettamente malvagie nelle macro.

Sono elaborazioni di testo e non hanno ambito. Se #define foo 1 , qualsiasi uso successivo di foo come identificatore fallirà. Questo può portare a strani errori di compilazione e bug di runtime difficili da trovare.

Non accettano argomenti in senso normale. Puoi scrivere una funzione che prenderà due valori int e restituirà il massimo, perché gli argomenti verranno valutati una volta e i valori usati successivamente. Non puoi scrivere una macro per farlo, perché valuterà almeno un argomento due volte e fallirà con qualcosa come max (x ++, --y) .

Ci sono anche insidie ??comuni. È difficile ottenere più dichiarazioni proprio in esse e richiedono molte parentesi forse superflue.

Nel tuo caso, hai bisogno di parentesi:

#define radian2degree(a) (a * 57.295779513082)

deve essere

#define radian2degree(a) ((a) * 57.295779513082)

e stai ancora calpestando chiunque scriva una funzione radian2degree in un ambito interno, sicuro che quella definizione funzionerà nel suo ambito.

Altri suggerimenti

La maggior parte delle altre risposte discute sul perché le macro siano malvagie, incluso il modo in cui il tuo esempio ha un difetto nell'uso delle macro. Ecco il punto di vista di Stroustrup: http://www.research.att.com/~bs /bs_faq2.html#macro

Ma la tua domanda era chiederti per quali macro sono ancora utili. Ci sono alcune cose in cui le macro sono migliori delle funzioni incorporate, ed è qui che stai facendo cose che semplicemente non possono essere fatte con funzioni incorporate, come:

  • incolla token
  • gestione di numeri di riga o simili (come per la creazione di messaggi di errore in assert () )
  • gestire cose che non sono espressioni (ad esempio quante implementazioni di offsetof () usano l'uso di un nome di tipo per creare un'operazione di cast)
  • la macro per ottenere un conteggio degli elementi dell'array (non è possibile farlo con una funzione, poiché il nome dell'array si riduce a un puntatore troppo facilmente)
  • creando cose simili a "tipo polimorfico" in C in cui i modelli non sono disponibili

Ma con un linguaggio che ha funzioni incorporate, gli usi più comuni delle macro non dovrebbero essere necessari. Sono anche riluttante a usare le macro quando ho a che fare con un compilatore C che non supporta le funzioni incorporate. E provo a non usarli per creare funzioni di tipo agnostico se possibile (creando invece diverse funzioni con un indicatore di tipo come parte del nome).

Ho anche iniziato a usare enum per costanti numeriche nominate anziché #define .

Per questa specifica macro, se la utilizzo come segue:

int x=1;
x = radian2degree(x);
float y=1;
y = radian2degree(y);

non ci sarebbe alcun controllo del tipo e x, y conterrà valori diversi.

Inoltre, il seguente codice

float x=1, y=2;
float z = radian2degree(x+y);

non farà ciò che pensi, dal momento che si tradurrà in

float z = x+y*0.017453292519;

anziché

float z = (x+y)+0.017453292519;

qual è il risultato atteso.

Questi sono solo alcuni esempi del comportamento scorretto e delle macro improprie.

Modifica

puoi vedere ulteriori discussioni su questo qui

Le macro vengono abusate relativamente spesso e si possono facilmente commettere errori usandole come mostrato dal tuo esempio. Prendi l'espressione radian2degree (1 + 1):

  • con la macro si espanderà a 1 + 1 * 57.29 ... = 58.29 ...
  • con una funzione sarà quello che vuoi che sia, ovvero (1 + 1) * 57.29 ... = ...

Più in generale, le macro sono malvagie perché sembrano funzioni quindi ti inducono ad usarle proprio come le funzioni ma hanno delle regole sottili . In questo caso, il modo corretto sarebbe quello di scriverlo sarebbe (notare la parentesi attorno a a):

#define radian2degree(a) ((a) * 57.295779513082)

Ma dovresti attenerti alle funzioni incorporate. Vedi questi collegamenti dalla FAQ Lite C ++ per ulteriori esempi di macro malvagie e le loro sottigliezze:

se possibile, utilizzare sempre la funzione in linea. Questi sono dattiloscritti e non possono essere facilmente ridefiniti.

define può essere ridefinito indefinito, e non c'è controllo del tipo.

Il preprocessore del compilatore è una cosa delicata, e quindi un terribile candidato per trucchi intelligenti. Come altri hanno sottolineato, è facile per il compilatore fraintendere le proprie intenzioni con la macro, ed è facile per voi fraintendere ciò che la macro farà effettivamente, ma, soprattutto, non è possibile passare alle macro nel debugger!

Le macro sono cattive perché potresti finire con il passare più di una variabile o uno scalare ad essa e questo potrebbe risolversi in un comportamento indesiderato (definisci una macro massima per determinare il massimo tra aeb, ma passa a ++ e b ++ alla macro e guarda cosa succede).

Se la tua funzione verrà comunque incorporata, non ci sono differenze di prestazioni tra una funzione e una macro. Tuttavia, esistono diverse differenze di usabilità tra una funzione e una macro, che favoriscono l'utilizzo di una funzione.

Se la macro viene compilata correttamente, non ci sono problemi. Ma se usi una funzione, il compilatore lo farà correttamente per te ogni volta. Quindi l'uso di una funzione rende più difficile la scrittura di codice errato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top