it-swarm.com.de

Warum Makros in C verwenden?

Mögliches Duplizieren:
Wofür sind C-Makros nützlich?

Alle paar Monate bekomme ich Lust, ein bisschen C zu lernen, das ich in meiner College-Programmierausbildung nie gelernt habe. Heute sind es Makros. Mein grundlegendes Verständnis von Makros ist, dass sie ein einfaches Suchen und Ersetzen sind, das in Ihrem Code vor vor dem Kompilieren geschieht. Ich habe Probleme, warum Sie würden Makros verwenden. Die meisten der grundlegenden Beispiele, die ich betrachte, sind so etwas wie

TEST(a,%d);
#define TEST(a,b) printf(" The value of " #a " = " #b " \n", a)

//which expands to 
printf(" The value of a = %d \n",a);

(Beispiel von hier )

Aus meiner Sicht als Neuling scheint die Definition einer neuen Funktion die gleichen Ergebnisse zu liefern. Ich kann sehen, wie historisch gesehen Makros nützlich wären, um eine Menge Quellcode in den Tagen vor dem einfachen Suchen und Ersetzen schnell zu ändern, aber irgendetwas sagt mir, dass ich einen größeren Punkt vermisse.

Welche nützlichen Dinge können Makros für Sie tun?

47
Alan Storm

Ein Grund ist, dass das Inline-Schlüsselwort bis C99 kein Standard in der Sprache C war. So konnten Sie mit Makros kleine Funktionen integrieren. Sie funktionieren auch in gewisser Weise wie Vorlagen, dh. Sie müssen in der Makrodefinition keine Typen angeben, zB:

#define MAX(x,y) ((x) > (y) ? (x) : (y))

Dieses Makro ist für Ganzzahlen, Doppelte, Floats usw. geeignet.

43
DeusAduro

Es ist nicht genau das Suchen und Ersetzen, sondern die Erweiterung des Tokens. C-Makros sind das, was jede andere Art von Makro in der Computerwelt ist: Eine Möglichkeit, kurz und einfach etwas zu schreiben und es automatisch in etwas längeres und komplizierteres zu verwandeln. 

Ein Grund, warum Makros verwendet werden, ist die Leistung. Sie sind ein Weg, um Funktionsaufrufe zu vermeiden, da sie im Gegensatz zu dem Schlüsselwort "Inline", das ein häufig ignorierter Hinweis für den Compiler ist, immer inline erweitert werden und nicht existieren (im Standard) vor C99. Sehen Sie sich beispielsweise die FD_-Makrofamilie an, die in Verbindung mit den von select und pselect verwendeten fd_sets verwendet wird. Diese fd_sets sind eigentlich nur Bitsets, und die FD_-Makros verbergen wenig zwielichtige Operationen. Es wäre ärgerlich, wenn Sie jedes Mal das Bit ausschreiben, das Sie selbst zwitschern, und ein Funktionsaufruf wäre für eine so schnelle Operation viel Aufwand, wenn er nicht eingebettet wäre.

Außerdem können Makros einige Funktionen ausführen, die Funktionen nicht bieten können. Betrachten Sie das Einfügen von Token. Da der Präprozessor vor dem Compiler läuft, kann er neue Bezeichner für den Compiler erstellen. Auf diese Weise können Sie viele ähnliche Definitionen erstellen, z.

#define DEF_PAIR_OF(dtype) \
  typedef struct pair_of_##dtype { \
       dtype first; \
       dtype second; \
  } pair_of_##dtype##_t 

 DEF_PAIR_OF(int);
 DEF_PAIR_OF(double);
 DEF_PAIR_OF(MyStruct);
 /* etc */

Eine Funktion, die eine Funktion nicht ausführen kann, besteht darin, Informationen zur Kompilierungszeit in Laufzeitinformationen umzuwandeln:

#ifdef DEBUG
#define REPORT_PTR_VALUE(v) printf("Pointer %s points to %p\n", #v, v)
#else 
#define REPORT_PTR_VALUE(v)
#endif

void someFunction(const int* reallyCoolPointer) {
  REPORT_PTR_VALUE(reallyCoolPointer);
  /* Other code */
}

Es gibt keine Möglichkeit, dass eine Funktion den Namen ihres Parameters in ihrer Ausgabe verwenden kann wie das Makro. Dies zeigt auch das Kompilieren von Debug-Code für Release-Builds.

64
Tyler McHenry

Makros werden zur Übersetzungszeit erweitert. Sie sind richtig, dass sie häufig missbraucht werden, aber ein klassisches Beispiel in C wäre ein Makro zum Schreiben von Debugging-Meldungen, das (wenn der Debug-Modus zur Kompilierungszeit deaktiviert ist) keinen Code generiert und keine Verlangsamung verursacht.

15
Mike McQuaid

Manchmal möchten Sie protokollieren, jedoch nur im Debug-Modus. Also schreibst du so etwas wie:

#ifdef DEBUG
#define LOG_MSG(x) printf(x);
#else
#define LOG_MSG(X)
#endif

Manchmal möchten Sie einfach etwas deaktivieren oder einschalten, wenn Sie die Compile-Zeit ändern. Zum Beispiel, mein Unternehmen macht ein Co-Branding für unser Produkt und Partner bitten, die Dinge abzuschalten. Also machen wir so etwas wie:

#ifndef SPECIAL_PARTNER_BUILD
    DoSomethingReallyAwesome();
#endif

Bauen Sie dann mit -DSPECIAL_PARTNER_BUILD auf, wenn Sie den Partnern Tropfen geben.

Unter vielen anderen möglichen Gründen.

Manchmal möchten Sie nur das Tippen für Dinge speichern, die Sie immer und immer wieder tun. Einige Leute nehmen dies zu weit ( Husten MFC Husten ) und schreiben, was in Makros ihrer eigenen Sprache entspricht, um eine schwierige API zu entfernen. Dies macht das Debuggen zu einem Albtraum von Frikkin.

12
i_am_jorf

Makros können zu verschiedenen Zwecken verwendet werden.

Es ist sehr nützlich, Makros für alle magischen Zahlen oder Zeichenfolgen zu verwenden.

#define MY_MAGIC_NUM 57

/*MY_MAGIC_NUM used all through out the code*/
5
lillq

C-Makros können zur Kompilierzeit Code erzeugen. Dies kann (ab) zum effektiven Erstellen domänenspezifischer Sprachen mit neuen Schlüsselwörtern und Verhaltensweisen verwendet werden.

Eines meiner Lieblingsbeispiele ist Simon Tathams Methode von Implementierung von Coroutinen in C durch Makros.

4
Dour High Arch

Wir haben diese Art von Makro in unserem Code verwendet:

// convenience macros for implementing field setter/getter functions
#ifndef CREATE_GET_SET
#define CREATE_GET_SET(PREFIX, FIELD_NAME,FIELD_DATATYPE) \
  protected:\
    FIELD_DATATYPE PREFIX ## FIELD_NAME;\
  public:\
  inline FIELD_DATATYPE get ## _ ## FIELD_NAME(void) const\
  { \
    return(PREFIX ## FIELD_NAME); \
  } \
  inline void set ## _ ## FIELD_NAME(FIELD_DATATYPE p) \
  { \
    PREFIX ## FIELD_NAME = p; \
  }
#endif

innerhalb einer Klasse/Struktur würden Sie eine Variable definieren:

CREATE_GET_SET(_, id, unsigned int);

Dies würde Ihre Variable definieren und den generischen Getter/Setter für den Code erstellen. Es sorgt einfach für eine sauberere, konsistente Codegenerierung für get/set. Sicher, Sie können alles aufschreiben, aber das ist eine Menge Boilerplate-Code. HINWEIS: Dies ist nur eines von ein paar Makros. Ich habe sie nicht alle gepostet. Sagen Sie "char *" nicht auf diese Weise (wenn Sie möchten, dass das Set die Daten strncpy oder strcpy setzt). Dies war nur eine einfache Demonstration dessen, was Sie mit einem Makro und einigen einfachen Typen tun können.

2
jmq

Ich denke, dass der größte Vorteil entsteht, wenn er als "Einstellungsdatei" verwendet wird.

#define USE_FAST_AGORHITM
//#define USE_SLOW_ONE

Und globale "Konstanten"

#define MAX_NUMBER_OF_USERS 100000

Es gibt Programme, die meistens in Makros geschrieben sind (Check for Instanz Brian Gladmans AES-Implementierung http://www.gladman.me.uk/cryptography_technology/index.php )

1
Luka Rahne

In leistungsintensiven Szenarien können Sie Makros verwenden, um eine "automatische" Schleifenabwicklung zu erstellen. Moderne Compiler machen dies zwar wahrscheinlich besser, aber das war früher eine nützliche Anwendung dafür.

1
korona

Der Compiler kennt oft Details über den Zielcomputer, sodass Sie die bedingte Kompilierung verwenden können, um einen Code für Big-Endian-Prozessoren und einen anderen für Little-Endian-Prozessoren zu verwenden. Dies verhindert, dass der Code mit Code, der für einen anderen Prozessor entwickelt wurde, aufgebläht wird.

Ein ähnlicher Fall liegt vor, wenn Sie für ein bestimmtes System Assembly-Code haben, für andere Systeme jedoch C-Code.

1
Nosredna

Bei Makros wird der Funktionscode in den Codestrom des Aufrufers eingefügt. Dies kann, abhängig von vielen anderen Dingen, die Leistung verbessern, da das Optimierungsprogramm den aufgerufenen Code prozedural integrieren kann - den aufgerufenen Code in den Aufrufer optimieren. __ Inline-Funktionen (wenn Sie auch C++ verwenden) und ein wenig Makros ist . http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.5

0
Diego Dias