Che cos'è una macro? Differenza tra macro e funzione?


16

Non capisco bene il concetto macro. Che cos'è una macro? Non capisco come sia diverso dalla funzione? Sia la funzione che la macro contengono un blocco di codice. In che modo differiscono macro e funzione?


15
Ti riferisci a un particolare linguaggio di programmazione?
mkrieger1,

15
Devi essere più specifico; macro fa riferimento a tre concetti molto diversi, approssimativamente macro di testo / preprocessore in stile C, macro di Lisp e macro di applicazioni.
Chrylis

Risposte:


7

Nota

Vorrei aggiungere il seguente chiarimento dopo aver osservato il modello di voto polarizzante su questa risposta.

La risposta non è stata scritta tenendo conto dell'accuratezza tecnica e dell'ampia generalizzazione. È stato un modesto tentativo di spiegare in un linguaggio semplice la differenza tra macro e funzioni a un neofita della programmazione, senza cercare di essere completo o accurato (in realtà è tutt'altro che accurato). Il linguaggio di programmazione C era nella mia mente durante la stesura della risposta, ma le mie scuse per non averlo menzionato chiaramente e aver causato una (potenziale) confusione.

Considero davvero la risposta condivisa da Jörg W Mittag . È stato perspicace leggerlo (quanto meno lo so) e l'ho votato subito dopo che è stato pubblicato. Ho appena iniziato lo scambio di stack di ingegneria del software e l'esperienza e le discussioni finora sono state davvero approfondite.

Lascerò questa risposta qui perché può essere utile per altri neofiti dello sviluppo software, cercando di capire un concetto senza impantanarsi nelle accuratezze tecniche.


Sia la macro che la funzione rappresentano un'unità di codice autonoma. Entrambi sono strumenti che aiutano nella progettazione modulare di un programma. Dal punto di vista del programmatore che sta scrivendo il codice sorgente, sembrano abbastanza simili. Tuttavia, differiscono nel modo in cui vengono gestiti durante il ciclo di vita dell'esecuzione del programma.

Una macro viene definita una volta e utilizzata in molti punti di un programma. La macro viene espansa in linea durante la fase di pre-elaborazione. Pertanto, tecnicamente non rimane un'entità separata una volta compilato il codice sorgente. Le istruzioni in definizione macro diventano parte delle istruzioni del programma, proprio come le altre istruzioni.

Il motivo dietro la scrittura di una macro è di rendere più semplice la scrittura e la gestione del codice sorgente per il programmatore. Le macro sono generalmente desiderate per attività più semplici in cui la scrittura di una funzione a tutti gli effetti sarebbe una penalità di prestazioni / runtime. Esempi di situazioni in cui una macro è preferibile alla funzione sono:

  • Utilizzo di valori costanti (come valori matematici o scientifici) o di alcuni parametri specifici del programma.

  • Stampa dei messaggi di registro o gestione delle asserzioni.

  • Esecuzione di semplici calcoli o controlli delle condizioni.

Quando si utilizza la macro, è facile apportare modifiche / correzioni in un posto che sono immediatamente disponibili ovunque venga utilizzata la macro nel programma. È necessaria una semplice ricompilazione del programma per rendere effettive le modifiche.

Il codice funzione invece viene compilato come unità separata all'interno del programma e viene caricato in memoria durante l'esecuzione del programma solo se necessario. Il codice funzione mantiene la sua identità indipendente dal resto del programma. Il codice caricato viene riutilizzato se la funzione viene chiamata più di una volta. Quando la chiamata di funzione viene rilevata nel programma in esecuzione, il controllo gli viene passato dal sottosistema di runtime e il contesto del programma in esecuzione (indirizzo istruzione di ritorno) viene conservato.

Tuttavia, c'è una leggera penalità prestazionale che deve essere riscontrata quando si chiama una funzione (cambio di contesto, conservazione dell'indirizzo di ritorno delle principali istruzioni del programma, passaggio dei parametri e gestione dei valori di ritorno ecc.). Quindi, l'uso della funzione è desiderato solo per blocchi complessi di codice (rispetto alle macro che gestiscono casi più semplici).

Con l'esperienza, un programmatore prende una decisione giudiziosa sul fatto che un pezzo di codice sia adatto come macro o funzione nell'architettura generale del programma.


9
Che dire della funzione di allineamento?
Nathan Cooper,

1
Nel linguaggio C & co anche una macro può essere una chiamata di funzione. Quindi fare una distinzione tra i due non ha molto senso.
Sombrero Chicken,

5
E le macro in stile C sono tutt'altro che autosufficienti: non introducono un ambito lessicale e hanno accesso illimitato ai nomi locali nell'ambito al momento della sostituzione.
Inutile

@Sombrero Chicken: forse potresti mostrarne un esempio? Puoi avere macro che la funzione CONTAIN chiama, ma (AFAIK) la macro non è una chiamata di funzione.
jamesqf,

3
Ci sono molte cose su questa risposta che sono fuorvianti. Gli esempi di cui parli sono assolutamente non in alcun modo le situazioni in cui le macro sono 'preferibili' - piuttosto il contrario. Le macro non dovrebbero essere usate per queste cose a meno che non ci sia un motivo estremamente valido per farlo. Le prestazioni non sono generalmente una buona logica per l'utilizzo delle macro. Per le ragioni menzionate in altri commenti, giustificare l'uso delle macro in questo modo è probabile che si traduca in un codice soggetto a errori con ogni sorta di conseguenze involontarie, in particolare in una base di codice più ampia.
Ben Cottrell,

65

Sfortunatamente, ci sono molteplici usi diversi del termine "macro" nella programmazione.

Nella famiglia di lingue Lisp e lingue ispirate da esse, così come molti linguaggi moderni di ispirazione funzionale o funzionale come Scala e Haskell, così come alcuni linguaggi imperativi come Boo, una macro è un pezzo di codice che viene eseguito al momento della compilazione (o almeno prima del runtime per implementazioni senza un compilatore) e può trasformare l'albero di sintassi astratto (o qualunque sia l'equivalente in un particolare linguaggio, ad esempio in Lisp, sarebbe la S-Expressions) in qualcos'altro durante la compilazione. Ad esempio, in molte implementazioni dello schema, forè una macro che si espande in più chiamate al corpo. Nei linguaggi tipizzati staticamente, le macro sono spesso sicure per il tipo, cioè non possono produrre codice che non sia ben digitato.

Nella famiglia di lingue C, le macro sono più simili alla sostituzione testuale. Il che significa anche che possono produrre codice che non è ben scritto o che non è sintatticamente legale.

Nei macroassemblatori, le "macro" si riferiscono a "istruzioni virtuali", ovvero istruzioni che la CPU non supporta in modo nativo ma che sono utili, quindi l'assemblatore consente di utilizzare tali istruzioni e le espande in più istruzioni che la CPU comprende .

Nello scripting dell'applicazione, una "macro" si riferisce a una serie di azioni che l'utente può "registrare" e "riprodurre".

Tutti questi sono in qualche modo tipi di codice eseguibile, il che significa che possono in qualche modo essere visti come funzioni. Tuttavia, nel caso delle macro Lisp, ad esempio, i loro input e output sono frammenti di programma. Nel caso di C, i loro input e output sono token. I primi tre hanno anche l'importantissima distinzione che vengono eseguiti in fase di compilazione . In effetti, le macro del preprocessore C, come suggerisce il nome, vengono effettivamente eseguite prima che il codice raggiunga anche il compilatore .


1
"Nei linguaggi tipicamente statici, le macro sono spesso sicure per i tipi, cioè non possono produrre codice che non sia ben digitato" - davvero? In quali lingue ti riferisci? AFAICS, questo è generalmente possibile garantire solo in un linguaggio tipicamente dipendente. In Haskell, le macro TH sono solo sintatticamente sicure, ma il controllo del tipo viene eseguito successivamente. (Quindi dal punto di vista del tipo, offrono ancora meno garanzie rispetto ai modelli C ++).
lasciato il

1
A parte lo scripting dell'applicazione, non credo che i tre esempi forniti siano così diversi. Tutti eseguono una sostituzione di qualche tipo nel programma, anziché saltare in un pezzo di codice condiviso quando viene eseguito.
IMSoP,

Forse puoi estendere la menzione delle macro di C con il concetto generale di linguaggi macro come M4, poiché per C è fondamentalmente che la specifica definisce un linguaggio macro ausiliario CPP e ne standardizza l'uso con C.
JoL

1
Il tema generale sembra essere che le macro generino codice da interpretare come parte di un codice sorgente di un programma più grande, anziché essere eseguito quando il programma viene eseguito.
jpmc26,

1
@ jpmc26 Direi che il tema generale è la sostituzione in cui fai una piccola cosa e si espande in una grande cosa. Il linguaggio macro M4 non è particolarmente pensato per il codice. Puoi usarlo per generare qualsiasi testo. Ci sono anche macro da tastiera, come questa risposta menzionata. Premi 1 o 2 tasti e questi si espandono in un numero maggiore di tasti premuti. anche le macro vim sono così. Salvare una grande sequenza di comandi in modalità normale in un unico tasto e si chiama quella sequenza eseguendo il comando in modalità normale che la esegue.
JoL

2

Nella famiglia di linguaggi C una definizione di macro , un comando preprocessore, specifica un modello di codice parametrizzato che viene sostituito alla chiamata di macro senza essere compilato nella definizione. Ciò significa che tutte le variabili libere devono essere associate nel contesto della chiamata macro. Argomenti dei parametri con un effetto collaterale simile i++potrebbero essere ripetuti, mediante un uso plurale del parametro. La sostituzione testuale dell'argomento 1 + 2di alcuni parametri xavviene prima della compilazione e potrebbe causare comportamenti imprevisti x * 3( 7io 9). Un errore nel corpo macro della definizione macro verrà mostrato solo al momento della compilazione alla chiamata macro.

La definizione della funzione specifica il codice con variabili libere legate al contesto del corpo della funzione; non la chiamata di funzione .

Tuttavia, la macro apparentemente negativa fornisce l'accesso alla chiamata, al numero di riga e al file di origine, all'argomento come stringa .


2

In termini leggermente più astratti, una macro è sintassi come una funzione è dati. Una funzione (in astratto) incapsula alcune trasformazioni sui dati. Prende i suoi argomenti come dati valutati, esegue alcune operazioni su di essi e restituisce un risultato che è anche solo dati.

Una macro al contrario prende una sintassi non valutata e opera su quella. Per i linguaggi di tipo C, la sintassi arriva a livello di token. Per le lingue con macro simili a LISP, ottengono la sintassi rappresentata come AST. La macro deve restituire un nuovo blocco di sintassi.


0

Una macro si riferisce generalmente a qualcosa che viene espanso in atto , sostituendo la macro "chiamata" durante la compilazione o la pre-elaborazione con singole istruzioni nella lingua di destinazione. In fase di esecuzione, in genere non vi sarà alcuna indicazione di dove inizi e finisca la macro.

Questo è distinto da una subroutine , che è un pezzo di codice riutilizzabile che si trova separatamente in memoria, a cui viene passato il controllo in fase di esecuzione . Le "funzioni", le "procedure" e i "metodi" nella maggior parte dei linguaggi di programmazione rientrano in questa categoria.

Come discute la risposta di Jörg W Mittag , i dettagli esatti variano tra le lingue: in alcuni, come C, una macro esegue la sostituzione del testo nel codice sorgente; in alcuni, come Lisp, esegue la manipolazione di una forma intermedia come un albero di sintassi astratto. Ci sono anche alcune aree grigie: alcune lingue hanno una notazione per "funzioni inline", che sono definite come una funzione, ma espanse nel programma compilato come una macro.

La maggior parte delle lingue incoraggia i programmatori a ragionare su ciascuna subroutine in modo isolato, definendo contratti di tipo per input e output e nascondendo altre informazioni sul codice chiamante. Spesso si ha un impatto sulle prestazioni nel chiamare una subroutine che le macro non comportano e le macro possono essere in grado di manipolare il programma in modi che una subroutine non può fare. Le macro possono quindi essere considerate "di livello inferiore" rispetto alle subroutine, poiché operano su una base meno astratta.


0

La macro viene eseguita durante la compilazione e la funzione viene eseguita in fase di esecuzione.

Esempio:

#include <stdio.h>

#define macro_sum(x,y) (x+y)

int func_sum(x,y) {
    return x+y;
}

int main(void) {
    printf("%d\n", macro_sum(2,3));
    printf("%d\n", func_sum(2,3));
    return 0;
}

Quindi durante la compilazione il codice viene effettivamente cambiato in:

#include <stdio.h>

int func_sum(x,y) {
    return x+y;
}

int main(void) {
    printf("%d\n", (2+3));
    printf("%d\n", func_sum(2,3));
    return 0;
}
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.