Frage

Ich habe ein wenig von geschrieben C , und ich kann es gut genug lesen eine allgemeine Vorstellung davon zu bekommen, was es tut, aber jedes Mal, wenn ich erlebt habe ein Makro es mir völlig geworfen . Ich am Ende zu erinnern, was das Makro ist und ersetzen es in meinem Kopf, als ich las. Diejenigen, die mir begegnet sind, das waren intuitiv und einfach waren immer wie kleine Mini-Funktionen zu verstehen, so fragte ich mich immer, warum sie Funktionen nicht nur waren.

kann ich verstehe die Notwendigkeit, verschiedene Build-Typen zu definieren, für Debug oder Cross-Plattform baut im Preprocessor aber die Fähigkeit scheint willkürlich Substitutionen zu definieren, nur nützlich zu sein eine ohnehin schon schwierige Sprache noch schwieriger verständlich zu machen.

Warum wurde ein so komplexer Präprozessor für C eingeführt? Und hat jemand ein Beispiel davon verwendet, die ich zu verstehen, warum es scheint immer noch für Zwecke verwendet werden, um andere als einfach, wenn #DEBUG Stil bedingte Compilations?

Edit:

Nachdem noch eine Reihe von Antworten las ich es nicht einfach bekommen. Die häufigste Antwort ist Code Inline. Wenn das Schlüsselwort inline es dann nicht tun, entweder es hat einen guten Grund, es zu tun, nicht, oder die Umsetzung muss repariert werden. Ich verstehe nicht, warum ein ganz anderer Mechanismus erforderlich ist, dass „wirklich inline diesen Code“ (Seite bilde der Code geschrieben werden, bevor inline um war) bedeutet. Ich verstehe auch nicht die Idee, dass das erwähnte „Wenn es zu dumm in Funktion gesetzt zu werden“. Sicherlich jedes Stück Code, der eine Eingabe nimmt und eine Ausgabe erzeugt werden am besten in Funktion setzen. Ich glaube, ich kann bekomme es nicht, weil ich nicht auf die Mikro-Optimierungen verwendet würde schreiben C , aber der Präprozessor nur fühlt sich an wie eine komplexe Lösung auf ein paar einfachen Probleme.

War es hilfreich?

Lösung

  

ich am Ende zu erinnern, was das Makro ist und es in meinem Kopf zu ersetzen, als ich las.

Das scheint ein schlechtes Licht auf die Namensgebung der Makros zu reflektieren. Ich würde annehmen, dass Sie nicht den Präprozessor zu emulieren, wenn es sich um ein log_function_entry() Makro waren.

  

Diejenigen, die mir begegnet sind, das waren intuitiv und einfach waren immer wie kleine Mini-Funktionen zu verstehen, so fragte ich mich immer, warum sie nur Funktionen waren nicht.

In der Regel sollten sie sein, wenn sie nicht auf generische Parameter in Betrieb nehmen müssen.

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

wird mit einem < Operator auf jede Art arbeiten.

Weitere dass nur Funktionen, Makros können Sie Operationen mit Hilfe der Symbole in der Quelldatei ausführen. Das heißt, Sie einen neuen Variablennamen erstellen können, oder verweisen auf die Quelldatei und die Zeilennummer der Makro aktiviert ist.

In C99, Makros können Sie auch variadische Funktionen wie printf

nennen
#define log_message(guard,format,...) \
   if (guard) printf("%s:%d: " format "\n", __FILE__, __LINE__,__VA_ARGS_);

log_message( foo == 7, "x %d", x)

In dem das Format funktioniert wie printf. Wenn die Wache wahr ist, gibt es die Nachricht zusammen mit der Datei und die Zeilennummer, die die Nachricht gedruckt. Wenn es sich um eine Funktionsaufruf war, würde es die Datei nicht kennen und die Linie genannt Sie es aus, und eine vaprintf Verwendung wäre ein bisschen mehr Arbeit.

Andere Tipps

Dieser Auszug ziemlich fasst meine Sicht der Dinge, die von mehreren Möglichkeiten zu vergleichen, die Makros C verwendet werden, und wie sie in D zu implementieren.

kopiert von DigitalMars.com

  

Damals, als C erfunden wurde, Compiler   Technologie war primitiv. Installieren eines   Text Makro-Präprozessor auf der Vorderseite   Ende war eine einfache und einfache Weise,   viele leistungsstarke Funktionen hinzuzufügen. Das   zunehmende Größe und Komplexität   Programme haben, dass diese dargestellt   Features kommen mit vielen inhärenten   Probleme. D hat keine   Präprozessor; aber D bietet eine   scalable Mittel dasselbe zu lösen   Probleme.

Makros

Präprozessormakros hinzufügen leistungsstarke Funktionen und Flexibilität C. Aber sie haben einen Nachteil:

  • Makros hat kein Konzept des Umfangs; sie sind von dem Punkt der Definition bis zum Ende der Quelle gültig. Sie schnitten eine Schneise über H-Dateien, verschachtelten Code usw. Wenn Zehntausende von Zeilen von Makrodefinitionen #include'ing, wird es problematisch versehentliche Makroerweiterungen zu vermeiden.
  • Makros sind an den Debugger nicht bekannt. ein Programm mit symbolischen Daten zu debuggen versuchen, sich nur durch den Debugger untergraben wird über Makroerweiterungen zu wissen, nicht die Makros.
  • Makros machen es unmöglich, Quellcode tokenize, als früher Makro ändern kann beliebig Token wiederholen.
  • Die rein textliche Grundlage von Makros führt zu willkürlich und inkonsequent Nutzung, so dass Code mit Hilfe von Makros fehleranfällig. (Einige versuchen zu lösen diese mit Vorlagen in C++ eingeführt wurde.)
  • Makros sind noch nachholen Defizite in der Ausdrucksmöglichkeiten der Sprache, wie „Wrapper“ um die Header-Dateien verwendet.

Hier ist eine Aufzählung der häufigsten Anwendungen für Makros und die entsprechende Funktion in D:

  1. Definieren wörtliche Konstanten:

    • Die C Preprocessor Way

      #define VALUE 5
      
    • Die D Way

      const int VALUE = 5;
      
  2. Erstellen einer Liste von Werten oder Fahnen:

    • Die C Preprocessor Way

      int flags:
      #define FLAG_X  0x1
      #define FLAG_Y  0x2
      #define FLAG_Z  0x4
      ...
      flags |= FLAG_X;
      
    • Die D Way

      enum FLAGS { X = 0x1, Y = 0x2, Z = 0x4 };
      FLAGS flags;
      ...
      flags |= FLAGS.X;
      
  3. Funktion Aufrufkonventionen Einstellung:

    • Die C Preprocessor Way

      #ifndef _CRTAPI1
      #define _CRTAPI1 __cdecl
      #endif
      #ifndef _CRTAPI2
      #define _CRTAPI2 __cdecl
      #endif
      
      int _CRTAPI2 func();
      
    • Die D Way

      Aufrufkonventionen können in Blöcken angegeben werden, so gibt es keine Notwendigkeit, sie für jede Funktion zu ändern:

      extern (Windows)
      {
          int onefunc();
          int anotherfunc();
      }
      
  4. Einfache generische Programmierung:

    • Die C Preprocessor Way

      auswählen, die auf Textersetzung Basis-Funktion verwenden:

      #ifdef UNICODE
      int getValueW(wchar_t *p);
      #define getValue getValueW
      #else
      int getValueA(char *p);
      #define getValue getValueA
      #endif
      
    • Die D Way

      D ermöglichen Erklärungen von Symbolen, die Aliase von anderen Symbolen:

      version (UNICODE)
      {
          int getValueW(wchar[] p);
          alias getValueW getValue;
      }
      else
      {
          int getValueA(char[] p);
          alias getValueA getValue;
      }
      

Es gibt weitere Beispiele auf der DigitalMars Webseite .

Sie sind eine Programmiersprache (ein einfacheren) oben auf C, so dass sie zu tun metaprogramming in der Kompilierung nützlich sind ... in anderen Worten, können Sie Makrocode schreiben, die C-Code in weniger Linien und Zeit erzeugt, es dauern wird, es direkt in C geschrieben werden.

Sie sind auch sehr nützlich „Funktion wie“ Ausdrücke zu schreiben, die „polymorphe“ oder „überlastet“ sind; z.B. a max Makro definiert als:

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

ist nützlich für jeden numerischen Typ; und in C konnte man nicht schreiben:

int max(int a, int b) {return a>b?a:b;}
float max(float a, float b) {return a>b?a:b;}
double max(double a, double b) {return a>b?a:b;}
...

selbst wenn man wollte, weil Sie nicht Funktionen überlasten können.

Und nicht bedingte Compilierung und Datei einschließlich (das ist auch einen Teil der Makrosprache) ...

zu erwähnen,

Makros erlaubt jemand das Programmverhalten während der Kompilierung zu ändern. Bedenken Sie:

  • C Konstanten Programmverhalten zur Entwicklungszeit ermöglichen Festsetzung
  • C-Variablen das Verhalten des Programms zur Ausführungszeit verändern ermöglichen Änderungen
  • C-Makros Programmverhalten bei der Kompilierung verändern ermöglichen Änderungen

Bei der Kompilierung bedeutet, dass nicht verwendeter Code nicht einmal in die binären gehen wird und dass der Build-Prozess kann die Werte ändern, solange es mit dem Makro-Präprozessor integriert ist. Beispiel: make ARCH = Arm (annimmt Spedition Makrodefinition als cc -DARCH = Arm)

Einfache Beispiele: (Von glibc limits.h, definiert den größten Wert von long)

#if __WORDSIZE == 64
#define LONG_MAX 9223372036854775807L
#else
#define LONG_MAX 2147483647L
#endif

Prüft bei der Kompilierung (das #define __WORDSIZE verwenden), wenn wir 32 oder 64 Bit sind kompilieren. Mit einem multilib Toolchain, mit Hilfe von Parametern -m32 und -m64 automatisch Bitgröße ändern kann.

(POSIX-Version Anfrage)

#define _POSIX_C_SOURCE 200809L

Anfragen während der Kompilierung POSIX 2008 unterstützen. Die Standardbibliothek kann viele (nicht kompatibel) Standards unterstützen, aber mit dieser Definition wird die korrekte Funktion Prototypen zur Verfügung stellt (Beispiel: getline (), keine gets (), etc.). Wenn die Bibliothek die Norm nicht unterstützen kann er eine #error während der Kompilierung geben, anstatt während der Ausführung abstürzt, zum Beispiel.

(fest einprogrammiert Pfad)

#ifndef LIBRARY_PATH
#define LIBRARY_PATH "/usr/lib"
#endif

Definiert, während der Kompilierung ein hard Verzeichnis. Könnte mit -DLIBRARY_PATH = / home / user / lib zum Beispiel geändert werden. Wenn das ein const char * ist, wie würden Sie es während der Kompilierung konfigurieren?

(pthread.h, komplexe Definitionen bei der Kompilierung)

# define PTHREAD_MUTEX_INITIALIZER \
  { { 0, 0, 0, 0, 0, 0, { 0, 0 } } }

Große Stücke von Text können, die sonst nicht vereinfacht werden würde können (immer bei der Kompilierung) deklariert werden. Es ist nicht möglich, dies mit Funktionen oder Konstanten zu tun (zum Zeitpunkt der Kompilierung).

Um zu vermeiden, wirklich zu verkomplizieren Dinge und was darauf hindeutet, schlechte Codierung Arten zu vermeiden, ich bin gewohnt ein Beispiel-Code geben, die in unterschiedlichen, nicht kompatiblen Betriebssysteme kompiliert. Verwenden Sie Ihr Cross-Build-System für das, aber es sollte klar sein, dass der Präprozessor, dass aus dem Build-System ohne fremde Hilfe ermöglicht, ohne Kompilierung zu brechen aufgrund fehlender Schnittstellen.

Schließlich denkt über die Bedeutung der bedingten Kompilierung auf Embedded-Systeme, bei denen der Prozessorgeschwindigkeit und Speicher begrenzt sind und Systeme sind sehr heterogen.

Wenn Sie nun fragen, ist es möglich, alle Makro-Konstanten-Definitionen zu ersetzen und Funktionsaufrufe mit dem richtigen Definitionen? Die Antwort lautet ja, aber es wird einfach nicht die Notwendigkeit macht zum Ändern während der Kompilierung Programmverhalten geht weg. Der Präprozessor würde nach wie vor erforderlich.

Denken Sie daran,

, dass Makros (und der Pre-Prozessor) kommen aus den frühesten Tagen von C. Sie benutzten der einzige Weg, um inline zu tun ‚Funktionen‘ (denn natürlich, Inline ist ein sehr neues Schlüsselwort), und sie nach wie vor der einzige Weg sind, etwas FORCE inlined werden.

Auch Makros ist der einzige Weg, solche Tricks wie Einfügen die Datei und Zeile in String-Konstanten bei der Kompilierung tun können.

In diesen Tagen zu tun, die einzige Möglichkeit, viele der Dinge, die Makros verwendet wurden, sind besser durch neue Mechanismen behandelt. Aber sie haben immer noch ihren Platz, von Zeit zu Zeit.

Neben inlining für Effizienz und bedingte Kompilierung, kann Makros verwendet werden, um die Abstraktionsebene von Low-Level-C-Code zu erhöhen. C nicht wirklich Sie von den winzigsten Details von Speicher- und Ressourcenmanagement und genaue Layout der Daten und unterstützt sehr begrenzte Formen der Information Hiding und andere Mechanismen für die Verwaltung großer Systeme isolieren. Mit Hilfe von Makros, Sie sind nicht mehr beschränkt auf die Verwendung nur die Basiskonstrukte in der C-Sprache: Sie können Ihre eigenen Datenstrukturen und Codierung Konstrukte definieren, während immer noch nominell C schriftlich (einschließlich Klassen und Vorlagen!)!

Präprozessormakros bieten tatsächlich eine Turing-complete Sprache bei der Kompilierung ausgeführt. Einer der eindrucksvollen (und etwas beängstigend) Beispiele dafür ist über auf der C ++ Seite: die Erhöhung Preprocessor Bibliothek der C99 / C ++ 98 Vorprozessor (relativ) sicher Programmierkonstrukte zu bauen, die dann erweitert werden, was auch immer zugrundeliegende Erklärungen und Code-Eingabe, ob C oder C ++.

In der Praxis würde ich in Bezug auf Präprozessor Programmierung als letztes Mittel empfehlen, wenn Sie in sicheren Sprachen hohe Niveau Konstrukte nicht die Breite haben zu verwenden. Aber manchmal ist es gut zu wissen, was Sie tun können, wenn Ihr Rücken gegen die Wand und die Wiesel schließen in ...!

Computer-Dummheiten :

  

Ich habe diesen Code Auszug in vielen Freeware-Gaming-Programme gesehen für UNIX:

     

/ *
   * Bit-Werte.
   * /
   #define Bit_0 1 |    #define Bit_1 2
   #define BIT_2 4
   #define BIT_3 8
   #define BIT_4 16
   #define BIT_5 32
   #define BIT_6 64
   #define BIT_7 128
   #define BIT_8 256
   #define BIT_9 512
   #define BIT_10 1024
   #define BIT_11 2048
   #define BIT_12 4096
   #define BIT_13 8192
   #define BIT_14 16384
   #define BIT_15 32768
   #define BIT_16 65536
   #define BIT_17 131072
   #define BIT_18 262144
   #define BIT_19 524288
   #define BIT_20 1048576
   #define BIT_21 2097152
   #define BIT_22 4194304
   #define BIT_23 8388608
   #define BIT_24 16777216
   #define BIT_25 33554432
   #define BIT_26 67108864
   #define BIT_27 134217728
   #define BIT_28 268435456
   #define BIT_29 536870912
   #define BIT_30 1073741824
   #define BIT_31 2147483648

     

Ein einfacher Weg, dies zu erreichen, ist:

     

#define Bit_0 0x00000001
   #define Bit_1 0x00000002
   #define BIT_2 0x00000004
   #define BIT_3 0x00000008
   #define BIT_4 0x00000010
   ...
   #define BIT_28 0x10000000
   #define BIT_29 0x20000000
   #define BIT_30 0x40000000
   #define BIT_31 0x80000000

     

Eine einfachere Möglichkeit ist immer noch der Compiler zu lassen, die Berechnungen zu tun:

     

#define Bit_0 (1)
   #define Bit_1 (1 << 1)
   #define BIT_2 (1 << 2)
   #define BIT_3 (1 << 3)
   #define BIT_4 (1 << 4)
   ...
   #define BIT_28 (1 << 28)
   #define BIT_29 (1 << 29)
   #define BIT_30 (1 << 30)
   #define BIT_31 (1 << 31)

     

Aber warum gehen den ganzen Aufwand von 32 Konstanten definieren? Die Sprache C hat auch parametrisierte Makros. Alles, was Sie wirklich brauchen, ist:

     

#define BIT (x) (1 << (x))

     

Wie auch immer, ich frage mich, ob Mann, der den ursprünglichen Code schrieb einen Rechner verwendet oder berechnet einfach alles auf dem Papier aus.

Das ist nur eine mögliche Verwendung von Makros.

Eine der Fall, in dem Makros wirklich glänzen, wenn sie mit ihnen Code-Generation zu tun.

habe ich auf einem alten C ++ System zu arbeiten, das ein Plugin-System mit seinen eigenen Weg wurde mit Parameter zu übergeben, um das Plugin (eine benutzerdefinierte Karte artige Struktur verwenden). Einige einfache Makros wurden verwendet, um mit dieser Marotte beschäftigen und uns erlaubt, ohne zu viel Probleme realen C ++ Klassen und Funktionen mit normalen Parametern in den Plugins zu verwenden. Alle Glue-Code von Makros erzeugt werden.

Ich will hinzufügen, was ist schon gesagt worden ist.

Da Makros auf Text Ersetzungen arbeiten sie erlauben Sie tun sehr nützliche Dinge, die nicht möglich wäre, mit Funktionen zu tun.

Hier einige Fälle, in denen Makros können wirklich nützlich sein:

/* Get the number of elements in array 'A'. */
#define ARRAY_LENGTH(A) (sizeof(A) / sizeof(A[0]))

Dies ist ein sehr beliebtes und häufig verwendetes Makro. Das ist sehr praktisch, wenn Sie zum Beispiel muß durch ein Array zu durchlaufen.

int main(void)
{
    int a[] = {1, 2, 3, 4, 5};
    int i;
    for (i = 0; i < ARRAY_LENGTH(a); ++i) {
        printf("a[%d] = %d\n", i, a[i]);
    }
    return 0;
}

Dabei spielt es keine Rolle, ob ein anderer Programmierer fünf weitere Elemente ergänzt im decleration a. Die for-Schleife wird immer durchlaufen alle Elemente.

Die Funktionen der C-Bibliothek vergleichen Speicher und Strings sind ziemlich hässlich zu verwenden.

Sie schreiben:

char *str = "Hello, world!";

if (strcmp(str, "Hello, world!") == 0) {
    /* ... */
}

oder

char *str = "Hello, world!";

if (!strcmp(str, "Hello, world!")) {
    /* ... */
}

Um zu überprüfen, ob str Punkte "Hello, world". Ich persönlich denke, dass beide diese Lösungen aussehen ziemlich hässlich und verwirrend (vor allem !strcmp(...)).

Hier sind zwei ordentliche Makros einige Leute (einschließlich I) verwenden, wenn sie Strings oder Speicher vergleichen, müssen mit strcmp / memcmp:

/* Compare strings */
#define STRCMP(A, o, B) (strcmp((A), (B)) o 0)

/* Compare memory */
#define MEMCMP(A, o, B) (memcmp((A), (B)) o 0)

Jetzt können Sie nun den Code wie folgt schreiben:

char *str = "Hello, world!";

if (STRCMP(str, ==, "Hello, world!")) {
    /* ... */
}

Hier ist die Absicht, viel klarer!

Diese Fälle waren Makros für Dinge Funktionen verwendet werden, können nicht erreichen. Makros sollte nicht verwendet werden, um Funktionen zu ersetzen, aber sie haben andere gute Zwecke.

die Kommentare in Ihrer Frage gegeben, Sie nicht in vollem Umfang schätzen kann, dass eine Funktion aufrufen, kann eine angemessene Menge an Aufwand mit sich bringen. Die Parameter und Schlüsselregister können in auf den Stapel auf dem Weg kopiert werden müssen, und der Stapel aus auf dem Weg abgewickelt. Dies gilt insbesondere für die älteren Intel-Chips. Makros lassen Sie den Programmierer halten die Abstraktion einer Funktion (fast), vermied es aber, die teure Overhead eines Funktionsaufrufs. Das Inline-Schlüsselwort ist beratender, aber der Compiler kann nicht immer richtig. Der Ruhm und die Gefahr von ‚C‘ ist, dass Sie in der Regel die Compiler zu Ihrem Willen beugen können.

Ihr Brot und Butter, von Tag zu Tag Anwendungsprogrammierung diese Art von Mikro-Optimierung (Vermeidung von Funktionsaufrufe) ist in der Regel schlimmer als nutzlos, aber wenn Sie eine zeitkritische Funktion schreiben, durch den Kernel eines Betriebs genannt System, dann kann es einen großen Unterschied machen.

Im Gegensatz zu normalen Funktionen können Sie Steuerungsablauf tun (wenn, während, für, ...) in Makros. Hier ein Beispiel:

#include <stdio.h>

#define Loop(i,x) for(i=0; i<x; i++)

int main(int argc, char *argv[])
{
    int i;
    int x = 5;
    Loop(i, x)
    {
        printf("%d", i); // Output: 01234
    } 
    return 0;
} 

Es ist gut für Code inlining und Funktionsaufruf Overhead zu vermeiden. So wie es zu verwenden, wenn Sie das Verhalten später ohne Bearbeitung vieler Orte ändern mögen. Es ist nicht nützlich für komplexe Dinge, aber für einfache Codezeilen, die Sie Inline wollen, ist es nicht schlecht.

C Präprozessor Text Manipulation durch Nutzung kann man das C-Äquivalent von einer polymorphen Datenstruktur aufzubauen. Mit dieser Technik wir eine zuverlässige Tool-Box von primitiven Datenstrukturen konstruieren können, die in jedem C-Programm verwendet werden können, da sie die Vorteile von C-Syntax nehmen und nicht die Besonderheiten einer bestimmten Implementierung.

Ausführliche Erklärung, wie Makros zu verwenden, für die Datenstruktur der Verwaltung wird hier gegeben - http://multi-core-dump.blogspot.com/2010/11/interesting-use-of-c-macros-polymorphic.html

Makros können Sie loswerden Kopie kleistert Fragmente erhalten, die Sie nicht auf andere Weise beseitigen.

Zum Beispiel (der echten Code, Syntax von VS 2010-Compiler):

for each (auto entry in entries)
{
        sciter::value item;
        item.set_item("DisplayName",    entry.DisplayName);
        item.set_item("IsFolder",       entry.IsFolder);
        item.set_item("IconPath",       entry.IconPath);
        item.set_item("FilePath",       entry.FilePath);
        item.set_item("LocalName",      entry.LocalName);
        items.append(item);
    }

Dies ist der Ort, an dem Sie einen Feldwert unter dem gleichen Namen in ein Script-Engine übergeben. Ist diese Kopie kleistert? Ja. DisplayName wird als String für ein Skript und als Feldnamen für den Compiler verwendet. Ist das schlecht? Ja. Wenn Sie Sie Code und benennen LocalName zu RelativeFolderName Refactoring (wie ich) und vergessen, das gleiche mit der Zeichenfolge zu tun (wie ich), wird das Skript in einer Art und Weise arbeiten Sie nicht erwarten (in der Tat, es in meinem Beispiel hängt davon ab, haben Sie vergessen, das Feld in einem separaten Skript-Datei umbenennen, aber wenn das Skript für die Serialisierung verwendet wird, würde es sich um eine 100% Fehler).

sein

Wenn Sie einen Makro für diesen, wird es kein Platz für den Fehler sein:

for each (auto entry in entries)
{
#define STR_VALUE(arg) #arg
#define SET_ITEM(field) item.set_item(STR_VALUE(field), entry.field)
        sciter::value item;
        SET_ITEM(DisplayName);
        SET_ITEM(IsFolder);
        SET_ITEM(IconPath);
        SET_ITEM(FilePath);
        SET_ITEM(LocalName);
#undef SET_ITEM
#undef STR_VALUE
        items.append(item);
    }

Leider öffnet dies eine Tür für andere Arten von Fehlern. Sie können einen Tippfehler Schreiben des Makro machen und wird nie ein verwöhntes Code sehen, da der Compiler zeigt nicht, wie es nach allen Vorverarbeitung aussieht. Jemand anderes könnte den gleichen Namen verwenden (das ist, warum ich „Freigabe“ Makros so schnell wie möglich mit #undef). So verwenden Sie es mit Bedacht aus. Wenn Sie einen anderen Weg sehen Loswerden von kopier klebte Code (wie Funktionen), verwenden Sie diese Art und Weise. Wenn Sie sehen, dass mit Makros von kopier klebte Code loszuwerden ist das Ergebnis nicht wert, halten Sie die Kopie kleistert Code.

Einer der offensichtlichen Gründe ist, dass ein Makro unter Verwendung, wird der Code bei der Kompilierung erweitert werden, und Sie erhalten einen Pseudo-Funktionsaufruf ohne den Anruf-Overhead.

Sie können es auch für symbolische Konstanten verwenden, so dass Sie nicht den gleichen Wert an mehreren Stellen bearbeiten müssen, um eine kleine Sache zu ändern.

Macros .. denn wenn Ihr & # (* $ & Compiler weigert sich einfach nur etwas Inline.

Das sollte ein Motivationsplakat sein, nicht wahr?

In alle Ernst, google Präprozessor Missbrauch (möglicherweise eine ähnliche Frage sO als die # 1 Ergebnis sehen). Wenn ich einen Makro mich schreibe, die über die Funktionalität von assert geht (), versuche ich in der Regel zu sehen, ob mein Compiler tatsächlich eine ähnliche Funktion Inline würde.

Andere argumentieren, gegen die Verwendung von #if für die bedingte Kompilierung .. Sie würden eher Sie:

if (RUNNING_ON_VALGRIND)

statt

#if RUNNING_ON_VALGRIND

.. für Debugging-Zwecke, da Sie die if () sehen können, aber nicht in einem Debugger #IF. Dann tauchen wir in #ifdef vs #if.

Wenn seine unter 10 Zeilen Code, versuchen Sie es Inline. Wenn es nicht inlined werden kann, versuchen Sie es zu optimieren. Wenn es zu dumm, eine Funktion zu sein, einen Makro machen.

Während ich kein großer Fan von Makros bin und nicht dazu neigen, viel mehr C zu schreiben, basierend auf meinem aktuellen Tasking, so etwas wie dies (die offensichtlich einige Nebenwirkungen haben könnte) ist bequem:

#define MIN(X, Y)  ((X) < (Y) ? (X) : (Y))

Jetzt habe ich so etwas nicht in Jahren geschrieben, aber ‚Funktionen‘ wie das waren alle über Code, den ich früher in meiner Karriere gehalten. Ich denke, die Expansion bequem betrachtet werden könnte.

Ich hatte niemand dies so ist, in Bezug auf Funktion wie Makros zu erwähnen, zum Beispiel:

#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))

Generell ist es empfehlenswert, mit Hilfe von Makros zu vermeiden, wenn nicht notwendig, aus vielen Gründen, die Lesbarkeit ist das Hauptanliegen sein. Also:

  

Wann sollten Sie diese über eine Funktion verwenden?

Fast nie, da gibt es eine besser lesbare Alternative, die inline ist, finden Sie unter https : //www.greenend.org.uk/rjk/tech/inline.html oder http://www.cplusplus.com/articles/2LywvCM9/ (das zweite Verbindungs ist eine C ++ Seite, aber der Punkt ist, an c-Compiler so weit anwendbar, wie ich weiß).

Nun ist der geringe Unterschied, dass Makros durch den Pre-Prozessor gehandhabt werden und Inline wird vom Compiler behandelt, aber es gibt keinen praktischen Unterschied heute.

  

Wann ist es angebracht diese zu verwenden?

Für kleine Funktionen (zwei oder drei Liner max). Das Ziel ist, einen Vorteil während der Laufzeit eines Programms, als Funktion wie Makros (und Inline-Funktionen) ist Code Ersatz während der Pre-verarbeitend (oder Zusammenstellung bei inline) und sind nicht wirklich Funktionen in Gedenken getan zu gewinnen, so gibt es keinen Funktionsaufruf Overhead (mehr Details in den verknüpften Seiten).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top