Heim >Backend-Entwicklung >C++ >#define INC(a) INC(a ?

#define INC(a) INC(a ?

Susan Sarandon
Susan SarandonOriginal
2024-10-22 06:10:31578Durchsuche

#define INC(a) INC(a ?

Stürzt dieses Makro GCC ab? Lesen Sie und Sie werden die Antwort haben

Das Ziel dieses Artikels ist es, Sie in die großartige Welt der Makros in C einzuführen.

Eine Präprozessoranweisung

In C werden Zeilen, die mit einem # beginnen, vom Compiler beim Kompilieren der Quelldateien interpretiert. Diese werden Präprozessordirektiven genannt. Makros sind eines davon.

Kleiner historischer Punkt:

C-Sprachmakros wurden mit dem ersten C-Sprachstandard namens ANSI C (oder C89) eingeführt
die 1989 vom American National Standards Institute (ANSI) standardisiert wurde.

Vor dieser Standardisierung waren Makros jedoch bereits Teil der klassischen C- (oder K&R-C-)Sprache, die in den 1970er Jahren verwendet wurde
Der ursprüngliche C-Compiler, der von Dennis Ritchie für das UNIX-Betriebssystem entwickelt wurde, enthielt über den Präprozessor bereits eine rudimentäre Form von Makros, die Definitionen mit #define ermöglichten.

Definieren

#define SENS_DE_LA_VIE 3.14

/* ... */

printf("%f\n", SENS_DE_LA_VIE);

Die Funktionsweise der Definition ist recht einfach zu verstehen: Der Compiler ersetzt alle Vorkommen im Code durch den definierten Wert. Es funktioniert mit der folgenden Syntax: #define . Es ist eine Konvention, den Namen in Großbuchstaben zu schreiben, der Wert ist optional.

Ein bisschen wie „Strg-F und Ersetzen“.

Mama, das Makro

Wir können Defines verwenden, um Funktionen zu definieren, die wir in unserem Code verwenden können.

#define INC(a) a++ 
#define MULTI_LINE(a,b) a = b; \
                        b = 0; 


INC(my_variable); 
MULTI_LINE(my_variable, foobar) 
// Je souligne le fait qu'il peut ne pas y avoir de ';' en fin de ligne 

// Cela donnera  
my_variable++;
my_variable = foobar;
foobar = 0;

Ob oder nicht wenn

Wir können Makros bedingt deklarieren.
Wenn bereits ein Name definiert ist, führen wir den folgenden Code aus.

#ifdef DEBUG
// Je souligne qu'il est rarement conseillé d'utiliser des printf() en debug
// et que nous avons brisé la règle du nom des macros en MAJ.
#define return printf("(%s:%d)\n", __FUNCTION__, __LINE__);  return
#endif /* ! DEBUG */

int main(void) {
    return 1;
}

In diesem Fall verwende ich ein #ifndef, aber es existiert auch:

  • #ifdef
  • #if
  • #else
  • #elif
#if (X == 1)
#define Y 2
#elif (X == 2)
#define Y "Ami de la bonne blague, bonjour !"
#else
#define Y NULL
#endif /* ! X */

/* ... */

int main(void) {
    #if (X == 1)
    printf("%d\n", Y);
    #elif (X == 2)
    printf("%s\n", Y);
    #else
    printf("%p\n", Y);
    #endif /* ! X */
}

Wir signalisieren gerne das Ende von #if mit einem Massenkommentar. Dies ist eine Konvention, die Ihnen eine bessere Navigation im Code ermöglicht.

Vordefinierte Makros

Sie konnten im vorherigen Beispiel sehen, dass ich die Schlüsselwörter __FUNCTION__ und __LINE__ verwendet habe.
Wie Sie sich vorstellen können, handelt es sich dabei um Makros, die der Compiler durch den richtigen Wert ersetzt.

Es gibt eine Liste allgemeiner vordefinierter Makros.

Beachten Sie, dass es sogenannte systemspezifische Makros gibt.

Kleine, nicht erschöpfende Liste:

  • __DATUM__: 14. Januar 2012
  • __GNUC__: Hauptversion von GCC
  • __ZEIT__: 15:12:18
  • __INCLUDE_LEVEL__: Die Tiefe der Includes beginnend mit 0
  • __BASE_FILE__: Der Name der aktuellen Datei

Der Unendlichkeit und jenseits der Argumente entgegen

#define SENS_DE_LA_VIE 3.14

/* ... */

printf("%f\n", SENS_DE_LA_VIE);

Hier können wir sehen, dass wir verschiedene Makros generieren, die besonders nützlich beim Erstellen von Protokollen sind.
(Auch wenn es keine gute Idee ist, Protokolle mit printfs zu erstellen.)

X-Makro

Dazu müssen wir eine externe Datei erstellen, oft mit dem Namen *.def, obwohl es keine Konvention gibt.

#define INC(a) a++ 
#define MULTI_LINE(a,b) a = b; \
                        b = 0; 


INC(my_variable); 
MULTI_LINE(my_variable, foobar) 
// Je souligne le fait qu'il peut ne pas y avoir de ';' en fin de ligne 

// Cela donnera  
my_variable++;
my_variable = foobar;
foobar = 0;
#ifdef DEBUG
// Je souligne qu'il est rarement conseillé d'utiliser des printf() en debug
// et que nous avons brisé la règle du nom des macros en MAJ.
#define return printf("(%s:%d)\n", __FUNCTION__, __LINE__);  return
#endif /* ! DEBUG */

int main(void) {
    return 1;
}

Diese Art von Makro ist äußerst nützlich. Ich muss zugeben, dass es selten im Quellcode zu finden ist, aber es ermöglicht Ihnen, die Funktionsweise des Programms zu ändern, ohne den Quellcode ändern zu müssen. Interessanterweise wird es häufig bei der Erstellung von Kerneln verwendet. Es ermöglicht Ihnen, globale Strukturen wie IDT und GDT zu generieren.

Die Probleme

Achtung: Kurze Klarstellung zuerst, Makros sind tolle Werkzeuge, aber man muss vorsichtig sein. Sie sollten diese Art von Makro auf keinen Fall verwenden:

#if (X == 1)
#define Y 2
#elif (X == 2)
#define Y "Ami de la bonne blague, bonjour !"
#else
#define Y NULL
#endif /* ! X */

/* ... */

int main(void) {
    #if (X == 1)
    printf("%d\n", Y);
    #elif (X == 2)
    printf("%s\n", Y);
    #else
    printf("%p\n", Y);
    #endif /* ! X */
}

Nehmen wir ein Beispiel: MIN(2 5, fibo(25))

Problem Nr. 1

MIN(2 5, fibo(25)) => (2 5 < Fibo(25) ? 2 5: Fibo(25))

Hier liegt das Problem in der Berechnungspriorität. Der Compiler führt zuerst den Vergleich und dann die Addition durch, daher 2 (1). Wir korrigieren dies, indem wir mithilfe der Makroargumente Klammern hinzufügen.

// Ici, l'opérateur ## est l'opérateur de concaténation
#define DEBUG_PRNTF(fmt, ...) printf("LOG" ## fmt, __VA_ARGS__);

Da Sie nie wissen, was Ihre Benutzer als Parameter übergeben, setzen Sie die Argumente immer in Klammern.

Problem Nr. 2

MIN(2 5, fibo(25)) => (2 5 < Fibo(25) ? 2 5: Fibo(25))

Wir stellen fest, dass der Compiler eine dumme und unangenehme Ersetzung vornimmt, was bedeutet, dass wir fibo(25) zweimal berechnen. Ich lasse Sie sich vorstellen, ob es sich um eine rekursive Implementierung handelt.

Um dieses Problem zu beheben, deklarieren wir vor dem if eine Zwischenvariable.

Nützliche Makros

// color.def
X(NC, "\e[0m", "No Color", 0x000000) 
X(BLACK, "\e[0;30m", "Black", 0x000000) 
X(GRAY, "\e[1;30m", "Gray", 0x808080) 
X(RED, "\e[0;31m", "Red", 0xFF0000) 
X(LIGHT_RED, "\e[1;31m", "Light Red", 0xFF8080) 
X(GREEN, "\e[0;32m", "Green", 0x00FF00) 
X(LIGHT_GREEN, "\e[1;32m", "Light Green", 0x80FF80) 
X(BROWN, "\e[0;33m", "Brown", 0xA52A2A) 
X(YELLOW, "\e[1;33m", "Yellow", 0xFFFF00) 
X(BLUE, "\e[0;34m", "Blue", 0x0000FF) 
X(LIGHT_BLUE, "\e[1;34m", "Light Blue", 0xADD8E6) 
X(PURPLE, "\e[0;35m", "Purple", 0x800080) 
X(LIGHT_PURPLE, "\e[1;35m", "Light Purple", 0xEE82EE) 
X(CYAN, "\e[0;36m", "Cyan", 0x00FFFF) 
X(LIGHT_CYAN, "\e[1;36m", "Light Cyan", 0xE0FFFF) 
X(LIGHT_GRAY, "\e[0;37m", "Light Gray", 0xD3D3D3) 
X(WHITE, "\e[1;37m", "White", 0xFFFFFF)

Da haben wir Spaß

Hier handelt es sich um reinen Overkill-Code, nur zum Spaß. Ich empfehle Ihnen nicht unbedingt, diese Makros in Ihrem Code zu verwenden.
Ich habe einfach nur Spaß (eine gute Sache im Leben).

Ein kostenloses Auto

typedef struct {
    const char *name;        
    const char *ansi_code;  
    const char *description;
    unsigned int rgb;      
} Color;

#define X(NAME, ANSI, DESC, RGB) { #NAME, ANSI, DESC, RGB },
Color colors[] = {
    #include "color.def"
};
#undef X

#define X(NAME, ANSI, DESC, RGB) printf("%s (%s) = %s\n", #NAME, DESC, #RGB);
void print_colors() {
    // Bien entendu, on pourrait itérer sur la structure créée mais c'est une illustration
    #include "color.def"
}
#undef X

Ich lasse Sie mit einem kleinen -fsanitize=address testen. Es ist wirklich verrückt. Wir konnten sogar eine Verbesserung der Funktion auto_free feststellen, die als Parameter eine Zeichenfolge des Namens unserer Struktur verwendet, um einen Wechsel vorzunehmen.

Nehmen Sie sich Zeit

Chiller-Funktion, bei der wir lediglich die Ausführungszeit unserer Funktion berechnen. Sehr nützlich für Benchmarking.

#define SENS_DE_LA_VIE 3.14

/* ... */

printf("%f\n", SENS_DE_LA_VIE);

Fehler definieren

Kleines X-Makro, das ein Makro als Argument nimmt und es erweitert.

#define INC(a) a++ 
#define MULTI_LINE(a,b) a = b; \
                        b = 0; 


INC(my_variable); 
MULTI_LINE(my_variable, foobar) 
// Je souligne le fait qu'il peut ne pas y avoir de ';' en fin de ligne 

// Cela donnera  
my_variable++;
my_variable = foobar;
foobar = 0;

Automatisierte Testgenerierung

Hier generieren wir tatsächlich ganze Funktionen mit einem Makro, da C keine Grenzen kennt. Ich auch?

#ifdef DEBUG
// Je souligne qu'il est rarement conseillé d'utiliser des printf() en debug
// et que nous avons brisé la règle du nom des macros en MAJ.
#define return printf("(%s:%d)\n", __FUNCTION__, __LINE__);  return
#endif /* ! DEBUG */

int main(void) {
    return 1;
}

RTFM

Jetzt ist es an der Zeit, die Dinge zum Abschluss zu bringen. Wir haben viele wirklich coole Dinge gesehen. Und wenn Sie jemals in Versuchung geraten, können Sie die Makros selbst entdecken. Es gibt noch viel zu sehen.
Fazit also: RTFM.

PS: Was den Titel betrifft, sind Makros nicht rekursiv, sie erweitern sich nur mit einer Tiefe von 1 und in unserem vorliegenden Fall führt GCC eine implizite_Deklaration für INC aus und stürzt ab.

Das obige ist der detaillierte Inhalt von#define INC(a) INC(a ?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn