Ich verstehe das Makrokonzept nicht gut. Was ist ein Makro? Ich verstehe nicht, wie es sich von der Funktion unterscheidet? Sowohl Funktion als auch Makro enthalten einen Codeblock. Wie unterscheiden sich Makro und Funktion?
programming-practices
Edoardo Sorgentone
quelle
quelle
Antworten:
Hinweis
Ich möchte die folgende Klarstellung hinzufügen, nachdem ich das polarisierende Abstimmungsmuster zu dieser Antwort beobachtet habe.
Die Antwort wurde nicht unter Berücksichtigung der technischen Genauigkeit und der umfassenden Verallgemeinerung verfasst. Es war ein bescheidener Versuch, einem Programmieranfänger den Unterschied zwischen Makros und Funktionen in einfacher Sprache zu erklären, ohne zu versuchen, vollständig oder genau zu sein (in der Tat ist es alles andere als genau). Die Programmiersprache C hatte ich bei der Ausarbeitung der Antwort im Hinterkopf, entschuldigte mich jedoch dafür, dass ich sie nicht klar erwähnte und (potenzielle) Verwirrung stiftete.
Ich schätze die Antwort von Jörg W. Mittag sehr . Es war aufschlussreich, es zu lesen (wie wenig ich weiß) und ich habe es bald nach der Veröffentlichung bewertet. Ich habe gerade mit Software Engineering Stack Exchange angefangen und die bisherigen Erfahrungen und Diskussionen waren wirklich aufschlussreich.
Ich werde diese Antwort hier belassen, da sie für andere Softwareentwicklungs-Neulinge hilfreich sein kann, die versuchen, ein Konzept zu verstehen, ohne sich auf technische Genauigkeit einzulassen.
Sowohl Makro als auch Funktion repräsentieren eine in sich geschlossene Codeeinheit. Sie sind beide Werkzeuge, die beim modularen Aufbau eines Programms helfen. Aus der Sicht des Programmierers, der den Quellcode schreibt, sehen sie sich ziemlich ähnlich. Sie unterscheiden sich jedoch darin, wie sie während des Lebenszyklus der Programmausführung behandelt werden.
Ein Makro wird einmal definiert und an vielen Stellen in einem Programm verwendet. Makro wird in der Vorverarbeitungsphase inline erweitert. Somit bleibt es technisch keine separate Entität, sobald der Quellcode kompiliert ist. Die Anweisungen in der Makrodefinition werden wie andere Anweisungen Teil von Programmanweisungen.
Das Motiv beim Schreiben eines Makros besteht darin, dem Programmierer das Schreiben und Verwalten von Quellcode zu erleichtern. Makros sind im Allgemeinen für einfachere Aufgaben erwünscht, bei denen das Schreiben einer vollwertigen Funktion einen Performance-Overhead / Laufzeit-Nachteil bedeuten würde. Beispiele für Situationen, in denen ein Makro der Funktion vorgezogen wird, sind:
Verwenden von konstanten Werten (z. B. mathematischen oder wissenschaftlichen Werten) oder programmspezifischen Parametern.
Protokollnachrichten drucken oder Zusicherungen verarbeiten.
Durchführen einfacher Berechnungen oder Zustandsüberprüfungen.
Bei der Verwendung von Makros ist es einfach, Änderungen / Korrekturen an einer Stelle vorzunehmen, die sofort überall dort verfügbar sind, wo das Makro im Programm verwendet wird. Eine einfache Neukompilierung des Programms ist erforderlich, damit die Änderungen wirksam werden.
Funktionscode hingegen wird als separate Einheit innerhalb des Programms kompiliert und nur bei Bedarf während der Programmausführung in den Speicher geladen. Der Funktionscode behält seine vom Rest des Programms unabhängige Identität. Der geladene Code wird wiederverwendet, wenn die Funktion mehrmals aufgerufen wird. Wenn der Funktionsaufruf im laufenden Programm auftritt, wird die Steuerung vom Laufzeitsubsystem an ihn übergeben, und der Kontext des laufenden Programms (Rückgabeanweisungsadresse) bleibt erhalten.
Beim Aufrufen einer Funktion muss jedoch eine leichte Leistungseinbuße verzeichnet werden (Kontextwechsel, Beibehaltung der Rücksprungadresse der Hauptprogrammanweisungen, Übergabe von Parametern und Behandlung von Rückgabewerten usw.). Daher ist die Verwendung von Funktionen nur für komplexe Codeblöcke erwünscht (gegen Makros, die einfachere Fälle behandeln).
Mit der Erfahrung trifft ein Programmierer eine vernünftige Entscheidung, ob ein Teil des Codes als Makro oder Funktion in die gesamte Programmarchitektur passt.
quelle
Leider gibt es bei der Programmierung mehrere unterschiedliche Verwendungen des Begriffs "Makro".
In der Lisp-Familie von Sprachen und von ihnen inspirierten Sprachen sowie in vielen modernen funktionalen oder funktional inspirierten Sprachen wie Scala und Haskell sowie einigen imperativen Sprachen wie Boo ist ein Makro ein Stück Code, der zur Kompilierungszeit ausgeführt wird (oder zumindest vor der Laufzeit für Implementierungen ohne Compiler) und kann den Abstract Syntax Tree (oder was auch immer das Äquivalent in der jeweiligen Sprache ist, z. B. in Lisp, es wären die S-Expressions) während der Kompilierung in etwas anderes verwandeln. In vielen Schemaimplementierungen
for
ist ein Makro beispielsweise ein Makro, das sich zu mehreren Aufrufen an den Body erweitert. In statisch typisierten Sprachen sind Makros häufig typsicher, dh sie können keinen Code erzeugen, der nicht gut typisiert ist.In der C-Sprachfamilie ähneln Makros eher der Textsubstitution. Das bedeutet auch, dass sie Code produzieren können, der nicht gut typisiert oder syntaktisch nicht legal ist.
In Makro-Assemblern beziehen sich "Makros" auf "virtuelle Anweisungen", dh Anweisungen, die die CPU nicht von Haus aus unterstützt, die jedoch nützlich sind. Der Assembler ermöglicht Ihnen daher, diese Anweisungen zu verwenden und sie zu mehreren Anweisungen zu erweitern, die die CPU versteht .
In Anwendungsskripten bezieht sich ein "Makro" auf eine Reihe von Aktionen, die der Benutzer "aufzeichnen" und "wiedergeben" kann.
All dies sind in gewissem Sinne ausführbare Codes, was bedeutet, dass sie in gewissem Sinne als Funktionen angesehen werden können. Bei Lisp-Makros sind ihre Ein- und Ausgabe jedoch Programmfragmente. Im Fall von C sind ihre Eingabe und Ausgabe Token. Die ersten drei haben auch den sehr wichtigen Unterschied, dass sie zur Kompilierzeit ausgeführt werden . Tatsächlich werden C-Präprozessor-Makros, wie der Name schon sagt, tatsächlich ausgeführt, bevor der Code überhaupt den Compiler erreicht .
quelle
In der C-Sprachfamilie gibt eine Makrodefinition , ein Präprozessorbefehl, eine parametrisierte Codevorlage an, die beim Makroaufruf ersetzt wird, ohne bei der Definition kompiliert zu werden. Dies bedeutet, dass alle freien Variablen im Kontext des Makroaufrufs gebunden werden sollten. Parameterargumente mit einem Nebeneffekt wie
i++
könnten durch mehrfache Verwendung des Parameters wiederholt werden. Das Ersetzen von Argumenten1 + 2
einiger Parameter in Textformx
erfolgt vor dem Kompilieren und kann zu unerwartetem Verhalten führenx * 3
(7
io9
). Ein Fehler im Makrotext der Makrodefinition wird nur beim Kompilieren beim Makroaufruf angezeigt.Die Funktionsdefinition gibt Code mit freien Variablen an, die an den Kontext des Funktionskörpers gebunden sind. nicht der Funktionsaufruf .
Das anscheinend negative Makro bietet Zugriff auf den Aufruf, die Zeilennummer und die Quelldatei, das Argument als Zeichenfolge .
quelle
In etwas abstrakteren Begriffen handelt es sich bei einem Makro um Syntax und bei einer Funktion um Daten. Eine Funktion (in der Zusammenfassung) kapselt einige Transformationen für Daten. Es nimmt seine Argumente als ausgewertete Daten, führt einige Operationen an ihnen durch und gibt ein Ergebnis zurück, das ebenfalls nur Daten sind.
Ein Makro dagegen benötigt eine unbewertete Syntax und arbeitet damit. Bei C-ähnlichen Sprachen erfolgt die Syntax auf Token-Ebene. Für Sprachen mit LISP-ähnlichen Makros wird die Syntax als AST dargestellt. Das Makro muss einen neuen Syntaxblock zurückgeben.
quelle
Ein Makro bezieht sich im Allgemeinen auf etwas, das an Ort und Stelle erweitert wird und den Makro- "Aufruf" während der Kompilierung oder Vorverarbeitung durch einzelne Anweisungen in der Zielsprache ersetzt. Zur Laufzeit wird in der Regel nicht angegeben, wo das Makro beginnt und endet.
Dies unterscheidet sich von einer Subroutine , bei der es sich um einen wiederverwendbaren Code handelt, der sich separat im Speicher befindet und an den die Steuerung zur Laufzeit übergeben wird . Die "Funktionen", "Prozeduren" und "Methoden" in den meisten Programmiersprachen fallen in diese Kategorie.
Wie die Antwort von Jörg W. Mittag bespricht, variieren die genauen Details zwischen den Sprachen: In einigen, wie z. B. C, führt ein Makro eine Textsubstitution im Quellcode durch; In einigen Fällen, z. B. bei Lisp, wird eine Zwischenform wie ein abstrakter Syntaxbaum bearbeitet. Es gibt auch einige Grauzonen: Einige Sprachen haben eine Notation für "Inline-Funktionen", die wie eine Funktion definiert, aber wie ein Makro in das kompilierte Programm erweitert werden.
Die meisten Sprachen ermutigen Programmierer, über jedes Unterprogramm einzeln nachzudenken, Typverträge für Ein- und Ausgaben zu definieren und andere Informationen über den aufrufenden Code auszublenden. Das Aufrufen einer Unterroutine, die keine Makros enthält, hat häufig Leistungseinbußen, und Makros können das Programm möglicherweise auf eine Weise manipulieren, die eine Unterroutine nicht kann. Makros können daher als "untergeordnete Ebene" als Subroutinen betrachtet werden, da sie weniger abstrakt arbeiten.
quelle
Das Makro wird während der Kompilierung ausgeführt und die Funktion wird zur Laufzeit ausgeführt.
Beispiel:
Während der Kompilierung wird der Code also geändert in:
quelle