Funzioni inline vs macro del preprocessore


Risposte:


127

Le macro del preprocessore sono solo modelli di sostituzione applicati al codice. Possono essere utilizzati quasi ovunque nel codice perché vengono sostituiti con le loro espansioni prima che inizi la compilazione.

Le funzioni inline sono funzioni effettive il cui corpo viene iniettato direttamente nel sito di chiamata. Possono essere utilizzati solo dove una chiamata di funzione è appropriata.

Ora, per quanto riguarda l'utilizzo di macro e funzioni inline in un contesto simile a una funzione, tieni presente che:

  • Le macro non sono indipendenti dai tipi e possono essere espanse indipendentemente dal fatto che siano sinteticamente corrette: la fase di compilazione riporterà errori derivanti da problemi di espansione delle macro.
  • Le macro possono essere utilizzate in un contesto in cui non ti aspetti, con conseguenti problemi
  • Le macro sono più flessibili, in quanto possono espandere altre macro, mentre le funzioni inline non lo fanno necessariamente.
  • Le macro possono provocare effetti collaterali a causa della loro espansione, poiché le espressioni di input vengono copiate ovunque compaiano nel pattern.
  • Non è sempre garantito che le funzioni inline siano inline: alcuni compilatori lo fanno solo nelle build di rilascio o quando sono specificatamente configurati per farlo. Inoltre, in alcuni casi l'inlining potrebbe non essere possibile.
  • Le funzioni inline possono fornire l'ambito per le variabili (in particolare quelle statiche), le macro del preprocessore possono farlo solo nei blocchi di codice {...} e le variabili statiche non si comporteranno esattamente allo stesso modo.

39
Non è sempre garantito che le funzioni inline siano inline: perché il compilatore non sarà inline se così facendo genererà codice più lento, ecc. Il compilatore fa molte analisi che l'ingegnere non può e fa la cosa corretta.
Martin York

14
Credo che le funzioni ricorsive siano un altro esempio in cui la maggior parte dei compilatori ignora l'inlining.
LBushkin

Ci sono differenze importanti in C rispetto a C ++ in questo caso?
rzetterberg

7
Un punto non menzionato è che l'inlining può essere influenzato dai flag di compilazione. Ad esempio, quando si crea per la massima velocità (come GCC -O2 / -O3) il compilatore sceglierà di incorporare molte funzioni, ma quando si compila per la dimensione minima (-Os) se in genere le funzioni inline saranno chiamate solo una volta (o funzioni molto piccole ). Con le macro non esiste tale scelta.
dbrank0

Le macro non possono essere coperte con specificatore di accesso (come, privato o protetto) mentre le funzioni inline sono possibili.
Hit del

78

Innanzitutto, le macro del preprocessore sono semplicemente "copia incolla" nel codice prima della compilazione. Quindi non c'è controllo del tipo e possono comparire alcuni effetti collaterali

Ad esempio, se desideri confrontare 2 valori:

#define max(a,b) ((a<b)?b:a)

Gli effetti collaterali compaiono se si utilizza max(a++,b++)ad esempio ( ao bverranno incrementati due volte). Utilizza invece (ad esempio)

inline int max( int a, int b) { return ((a<b)?b:a); }

3
Voglio solo aggiungere al tuo esempio che oltre agli effetti collaterali, la macro può anche introdurre un carico di lavoro extra, considera che max(fibonacci(100), factorial(10000))quello più grande verrà calcolato due volte :(
watashiSHUN

Tutti parlano di controllo del tipo, ma hai solo fornito un esempio del mondo reale, ecco perché ho votato a favore di questa risposta.
Ivanzinho

16

La funzione Inline viene espansa dal compilatore mentre le macro vengono espanse dal Preprocessore, che è mera sostituzione testuale.

  • Non vi è alcun controllo del tipo durante il richiamo della macro mentre il controllo del tipo viene eseguito durante la chiamata della funzione.

  • Risultati indesiderati e inefficienza possono verificarsi durante l'espansione della macro a causa della rivalutazione degli argomenti e dell'ordine delle operazioni. Per esempio

    #define MAX(a,b) ((a)>(b) ? (a) : (b))
    int i = 5, j = MAX(i++, 0);

    risulterebbe in

    int i = 5, j = ((i++)>(0) ? (i++) : (0));
  • Gli argomenti macro non vengono valutati prima dell'espansione macro

    #define MUL(a, b) a*b
    int main()
    {
      // The macro is expended as 2 + 3 * 3 + 5, not as 5*8
      printf("%d", MUL(2+3, 3+5));
     return 0;
    }
    // Output: 16`
  • La parola chiave return non può essere utilizzata nelle macro per restituire valori come nel caso delle funzioni.

  • Le funzioni inline possono essere sovraccaricate

  • I token passati alle macro possono essere concatenati utilizzando l'operatore ## chiamato operatore Token-Pasing.

  • Le macro vengono generalmente utilizzate per il riutilizzo del codice in cui le funzioni inline vengono utilizzate per eliminare il sovraccarico di tempo (tempo in eccesso) durante la chiamata di funzione (evitando il salto a una subroutine).


13

La differenza fondamentale è il controllo del tipo. Il compilatore verificherà se ciò che si passa come valori di input è di tipi che possono essere passati alla funzione. Questo non è vero con le macro del preprocessore: vengono espanse prima di qualsiasi controllo del tipo e ciò può causare bug gravi e difficili da rilevare.

Qui ci sono molti altri punti meno ovvi delineati.


11

Per aggiungere un'altra differenza a quelle già fornite: non è possibile eseguire il passaggio a #definenel debugger, ma è possibile eseguire una funzione inline.



3

le funzioni inline sono simili alle macro (poiché il codice della funzione viene espanso nel punto della chiamata in fase di compilazione), le funzioni inline vengono analizzate dal compilatore, mentre le macro vengono espanse dal preprocessore. Di conseguenza, ci sono molte importanti differenze:

  • Le funzioni inline seguono tutti i protocolli di sicurezza dei tipi applicati alle funzioni normali.
  • Le funzioni inline vengono specificate utilizzando la stessa sintassi di qualsiasi altra funzione tranne che includono la parola chiave inline nella dichiarazione della funzione.
  • Le espressioni passate come argomenti alle funzioni inline vengono valutate una volta.
  • In alcuni casi, le espressioni passate come argomenti alle macro possono essere valutate più di una volta. http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx

  • le macro vengono espanse in fase di pre-compilazione, non è possibile utilizzarle per il debug, ma è possibile utilizzare funzioni inline.

- buon articolo : http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1

;


2

Una funzione inline manterrà la semantica del valore, mentre una macro del preprocessore copia solo la sintassi. Puoi ottenere bug molto sottili con una macro del preprocessore se usi l'argomento più volte - per esempio se l'argomento contiene una mutazione come "i ++", averla eseguita due volte è una sorpresa. Una funzione inline non avrà questo problema.


1

Una funzione inline si comporta sintatticamente proprio come una normale funzione, fornendo l'indipendenza dai tipi e un ambito per le variabili locali della funzione e l'accesso ai membri della classe se si tratta di un metodo. Inoltre, quando si chiamano metodi inline, è necessario rispettare le restrizioni private / protette.


1

Per conoscere la differenza tra macro e funzione inline , in primo luogo dovremmo sapere cosa sono esattamente e quando dovremmo usarli.

FUNZIONI :

int Square(int x){
return(x*X);
}
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • Alle chiamate di funzione è associato un sovraccarico, poiché dopo che la funzione termina l'esecuzione deve sapere dove deve tornare e deve anche memorizzare il valore nella memoria dello stack.

  • Per le piccole applicazioni non sarà un problema, ma prendiamo un esempio di applicazioni finanziarie in cui avvengono migliaia di transazioni ogni secondo, non possiamo andare con le chiamate di funzione.

MACRO:

# define Square(x) x*x;
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • Le macro funzionano nella fase di pre-elaborazione, ovvero in questa fase le istruzioni scritte con la parola chiave # verranno sostituite con il contenuto, ad es

int risultato = Quadrato (x * x)

Ma alle macro sono associati dei bug.

#define Square(x) x*x
int main() {
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

Qui l'uscita è 11 non 36 .

FUNZIONI IN LINEA :

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

int main() {
    using namespace std;
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

Uscita 36

La parola chiave inline richiede al compilatore di sostituire la chiamata di funzione con il corpo della funzione, qui l'output è corretto perché prima valuta l'espressione e poi viene passato Riduce l'overhead della chiamata di funzione poiché non è necessario memorizzare l'indirizzo di ritorno e lo stack la memoria non è richiesta per gli argomenti della funzione.

Confronto tra macro e funzioni inline:

  1. Le macro funzionano tramite sostituzione, mentre nelle funzioni inline, la chiamata di funzione viene sostituita con il corpo.
  2. Le macro sono soggette a errori a causa della sostituzione, mentre le funzioni inline sono sicure da usare.
  3. Le macro non hanno indirizzo mentre le funzioni inline hanno indirizzo.
  4. Le macro sono difficili da usare con più righe di codice, mentre le funzioni inline non lo sono.
  5. In C ++ le macro non possono essere utilizzate con funzioni membro mentre potrebbe esserlo la funzione inline.

CONCLUSIONE:

Le funzioni inline a volte sono più utili delle macro, poiché migliora le prestazioni ed è sicuro da usare e riduce anche il sovraccarico delle chiamate di funzione. È solo una richiesta al compilatore, alcune funzioni non saranno inline come:

  • grandi funzioni
  • funzioni con troppi argomenti condizionali
  • codice ricorsivo e codice con loop ecc.

il che è una buona cosa, perché è ogni volta che il compilatore pensa che sia meglio fare le cose in un altro modo.


Proprio come un'osservazione: la macro può essere fissata per restituire lo stesso numero tra parentesi. Tuttavia, è ancora soggetto a errori, poiché è necessario pensare all'assoluta sostituzione stupida ea tutti i casi durante l'implementazione.
mike

0

In GCC (non sono sicuro degli altri), dichiarare una funzione inline è solo un suggerimento per il compilatore. Alla fine della giornata spetta ancora al compilatore decidere se includere o meno il corpo della funzione ogni volta che viene chiamata.

La differenza tra le funzioni in linea e le macro del preprocessore è relativamente grande. Le macro del preprocessore sono solo la sostituzione del testo alla fine della giornata. Rinunci a molte delle possibilità per il compilatore di eseguire il controllo sul controllo del tipo sugli argomenti e sul tipo restituito. La valutazione degli argomenti è molto diversa (se le espressioni che passi alle funzioni hanno effetti collaterali, ti divertirai molto a fare il debug). Esistono sottili differenze su dove è possibile utilizzare le funzioni e le macro. Ad esempio se avessi:

#define MACRO_FUNC(X) ...

Dove MACRO_FUNC definisce ovviamente il corpo della funzione. È necessario prestare particolare attenzione affinché funzioni correttamente in tutti i casi può essere utilizzata una funzione, ad esempio una MACRO_FUNC scritta male causerebbe un errore in

if(MACRO_FUNC(y)) {
 ...body
}

Una funzione normale potrebbe essere utilizzata senza problemi lì.


0

Dal punto di vista della codifica, una funzione inline è come una funzione. Pertanto, le differenze tra una funzione inline e una macro sono le stesse delle differenze tra una funzione e una macro.

Dal punto di vista della compilazione, una funzione inline è simile a una macro. Viene iniettato direttamente nel codice, non chiamato.

In generale, dovresti considerare le funzioni inline come funzioni regolari con qualche ottimizzazione minore mescolata. E come la maggior parte delle ottimizzazioni, spetta al compilatore decidere se effettivamente si preoccupa di applicarle. Spesso il compilatore ignorerà felicemente qualsiasi tentativo da parte del programmatore di incorporare una funzione, per vari motivi.


0

le funzioni inline si comporteranno come una chiamata di funzione se in essa esiste un'istruzione iterativa o ricorsiva, in modo da impedire l'esecuzione ripetuta di istruzioni. È molto utile salvare la memoria complessiva del programma.


-1
#include<iostream>
using namespace std;
#define NUMBER 10 //macros are preprocessed while functions are not.
int number()
{ 
    return 10;
}
/*In macros, no type checking(incompatible operand, etc.) is done and thus use of micros can lead to errors/side-effects in some cases. 
However, this is not the case with functions.
Also, macros do not check for compilation error (if any). Consider:- */
#define CUBE(b) b*b*b
int cube(int a)
{
 return a*a*a;
}
int main()
{
 cout<<NUMBER<<endl<<number()<<endl;
 cout<<CUBE(1+3); //Unexpected output 10
 cout<<endl<<cube(1+3);// As expected 64
 return 0;
}

Le macro sono in genere più veloci delle funzioni in quanto non implicano l'effettivo sovraccarico delle chiamate di funzione.

Alcuni svantaggi delle macro: non c'è controllo del tipo Difficile da eseguire il debug in quanto causano una semplice sostituzione La macro non ha spazio dei nomi, quindi una macro in una sezione del codice può influenzare l'altra sezione. Le macro possono causare effetti collaterali come mostrato nell'esempio CUBE () sopra.

Le macro sono generalmente un rivestimento. Tuttavia, possono essere costituiti da più di una riga. Non ci sono tali vincoli nelle funzioni.


Quanto ti diverti di più #define TWO_N(n) 2 << ne poi cout << CUBE(TWO_N(3 + 1)) << endl;? (È meglio terminare le righe di output endlpiuttosto che iniziarle.)
Jonathan Leffler,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.