Und Sie implementieren hier keine der Standardformen der Vektormultiplikation (weder Skalarprodukt, Kreuzprodukt noch Tensorprodukt).
– David Thornley
20. November 2009 um 18:07 Uhr
Die komponentenweise Multiplikation hat ihren Nutzen, aber die durch das Makro auferlegte Dimensionsbegrenzung macht sie nicht so nützlich.
– aussen
20. November 2009 um 18:17 Uhr
@outis: Dies ist für die Verwendung in Bereichen vorgesehen, in denen die Abmessungsgrenze aufgrund der Art des Anwendungsbereichs streng festgelegt ist. Wie zum Beispiel in 3D-Grafiken.
– AnT steht zu Russland
20. November 2009 um 18:30 Uhr
Aber angenommen, die Anzahl der Dimensionen im Universum ändert sich, der Code wird brechen – das ist die Art von Kurzsichtigkeit, die zum Jahr-2000-Problem geführt hat.
– Martin Beckett
20. November 2009 um 18:41 Uhr
Ja … Es ist wie dieses berühmte Zitat aus dem Xerox-Handbuch. Einer der Vorteile der Deklaration von PI als benannte Konstante besteht darin, dass Sie den Code anpassen können, falls sich der Wert von Pi ändert.
– AnT steht zu Russland
20. November 2009 um 19:09 Uhr
AnT steht zu Russland
#define IMPLIES(x, y) (!(x) || (y))
#define COMPARE(x, y) (((x) > (y)) - ((x) < (y)))
#define SIGN(x) COMPARE(x, 0)
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a))
#define SWAP(x, y, T) do { T tmp = (x); (x) = (y); (y) = tmp; } while(0)
#define SORT2(a, b, T) do { if ((a) > (b)) SWAP((a), (b), T); } while (0)
#define SET(d, n, v) do{ size_t i_, n_; for (n_ = (n), i_ = 0; n_ > 0; --n_, ++i_) (d)[i_] = (v); } while(0)
#define ZERO(d, n) SET(d, n, 0)
Und natürlich diverse MIN, MAX, ABS etc.
Beachten Sie übrigens, dass keines der oben genannten Elemente durch eine Funktion in C implementiert werden kann.
PS Ich würde wahrscheinlich das oben genannte hervorheben IMPLIES Makro als eines der nützlichsten. Sein Hauptzweck besteht darin, das Schreiben eleganterer und lesbarer Behauptungen zu erleichtern, wie z
Ihr Makro-Fu ist beeindruckend (Sie klammern richtig und kennen die do...while(0) Technik), aber es gibt immer noch mehrere Bewertungsprobleme, die Sie wirklich nicht beheben können.
– David Thornley
20. November 2009 um 18:18 Uhr
@David Thornley: “Mehrfachbewertung” mit Makros gibt uns auch “faule Bewertung”. Es ist ebenso ein Problem wie ein Feature. Man muss nur lernen, es richtig zu benutzen. Zu sagen, dass die Gefahr der “Mehrfachauswertung” irgendwie bedeutet, dass man keine Makros verwenden soll, ist genau dasselbe wie zu sagen, dass man keinen Divisionsoperator verwenden soll, weil die Gefahr besteht, etwas durch Null zu dividieren.
– AnT steht zu Russland
20. November 2009 um 18:21 Uhr
@Tomas: das ist nicht nötig, aber dein Weg bedeutet, dass Anrufer, die tun wollen SWAPINT(*xp, *yp) muss jetzt prüfen xp != yp Erste. Heutzutage sehe ich nicht viel Sinn darin, einen unangenehmen Randfall hinzuzufügen, um (vielleicht) ein Register und / oder 4 Bytes Stapel zu sparen.
– Steve Jessop
20. November 2009 um 19:17 Uhr
Schön, ich mag “IMPLIES” am meisten 🙂
– Johannes Schaub – litb
20. November 2009 um 20:16 Uhr
@JPMC: SET weist einfach den angegebenen Wert zu v an alle n Elemente des Arrays d. ZERO Verwendet SET Wert zuzuweisen 0 an alle n Elemente des Arrays d.
– AnT steht zu Russland
29. Januar 2015 um 6:28 Uhr
Remo.D
Der entscheidende Punkt bei C-Makros ist ihre richtige Verwendung. Meiner Meinung nach gibt es drei Kategorien (ohne in Betracht zu ziehen, sie nur zu verwenden, um Konstanten beschreibende Namen zu geben).
Als Abkürzung für Codes, die man nicht wiederholen möchte
Stellen Sie eine allgemeine Verwendungsfunktion bereit
Ändern Sie die Struktur der C-Sprache (anscheinend)
Im ersten Fall lebt Ihr Makro nur in Ihrem Programm (normalerweise nur eine Datei), sodass Sie Makros wie das von Ihnen gepostete verwenden können, das nicht gegen doppelte Auswertung von Parametern und Verwendungen geschützt ist {...}; (potenziell gefährlich!).
Im zweiten Fall (und noch mehr im dritten) müssen Sie es sein äußerst Achten Sie darauf, dass sich Ihre Makros korrekt verhalten, als wären sie echte C-Konstrukte.
Das Makro, das Sie von GCC (min und max) gepostet haben, ist ein Beispiel dafür, sie verwenden die globalen Variablen _a und _b um das Risiko einer Doppelbewertung zu vermeiden (wie in max(x++,y++)) (Nun, sie verwenden GCC-Erweiterungen, aber das Konzept ist dasselbe).
Ich verwende Makros gerne, wenn es hilft, die Dinge klarer zu machen, aber sie sind ein scharfes Werkzeug! Wahrscheinlich haben sie deshalb einen so schlechten Ruf, ich denke, sie sind ein sehr nützliches Werkzeug und C wäre viel schlechter gewesen, wenn sie nicht vorhanden wären.
Ich sehe, dass andere Beispiele für Punkt 2 (Makros als Funktionen) bereitgestellt haben, lassen Sie mich ein Beispiel für die Erstellung eines neuen C-Konstrukts geben: die endliche Zustandsmaschine. (Ich habe dies bereits auf SO gepostet, aber ich kann es anscheinend nicht finden)
FSM {
STATE(s1):
... do stuff ...
NEXTSTATE(s2);
STATE(s2):
... do stuff ...
if (k<0) NEXTSTATE(s2);
/* fallthrough as the switch() cases */
STATE(s3):
... final stuff ...
break; /* Exit from the FSM */
}
Sie können Variationen zu diesem Thema hinzufügen, um den Geschmack von FSM zu erhalten, den Sie benötigen.
Jemand mag dieses Beispiel nicht mögen, aber ich finde es perfekt, um zu demonstrieren, wie einfache Makros Ihren Code lesbarer und aussagekräftiger machen können.
Dies ist so einfach zu missbrauchen, wenn Ihr Array plötzlich Heap-zugewiesen wird …
– Alexander C.
16. Juli 2010 um 14:08 Uhr
@Alexandre C. genauso leicht zu missbrauchen wie das Handbuch sizeof-mit-forloop-Weg. Ich vermute, man überprüft alle Aktionen in C besser 🙂
– Johannes Schaub – litb
16. Juli 2010 um 15:06 Uhr
Ja, mein Kommentar bezog sich auf das Idiom, nicht auf das Makro.
– Alexander C.
16. Juli 2010 um 15:09 Uhr
Was ist der Zweck der keep variabel, und warum tut foreach erweitern zu einem verschachtelten for Schleife?
– Todd Lehmann
22. Juli 2015 um 21:25 Uhr
@ToddLehman der Zweck ist es zu machen break funktioniert (und mein Code war kaputt. Ich dachte, ich hätte ihn von einer anderen Antwort von mir kopiert, aber anscheinend war ich nicht ganz wach genug: p. Ich habe es behoben, bitte überprüfen Sie es noch einmal)
– Johannes Schaub – litb
26. Juli 2015 um 12:38 Uhr
Wenn Sie Daten mehrmals in verschiedenen Kontexten definieren müssen, können Makros Ihnen dabei helfen, zu vermeiden, dass Sie dasselbe mehrmals neu auflisten müssen.
Angenommen, Sie möchten eine Aufzählung von Farben und eine Aufzählung-zu-String-Funktion definieren, anstatt alle Farben zweimal aufzulisten, könnten Sie eine Datei mit den Farben erstellen (Farben.def):
c(red)
c(blue)
c(green)
c(yellow)
c(brown)
Jetzt können Sie in Ihrer c-Datei Ihre Enum- und Ihre String-Konvertierungsfunktion definieren:
enum {
#define c(color) color,
# include "colors.def"
#undef c
};
const char *
color_to_string(enum color col)
{
static const char *colors[] = {
#define c(color) #color,
# include "colors.def"
#undef c
};
return (colors[col]);
};
Clifford
#if defined NDEBUG
#define TRACE( format, ... )
#else
#define TRACE( format, ... ) printf( "%s::%s(%d)" format, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__ )
#endif
Beachten Sie das Fehlen eines Kommas zwischen "%s::%s(%d)" und format ist absichtlich. Es gibt eine formatierte Zeichenfolge mit vorangestelltem Quellspeicherort aus. Ich arbeite in Echtzeit-Embedded-Systemen, so oft füge ich auch einen Zeitstempel in die Ausgabe ein.
Sollte nicht der NDEBUG sein #define TRACE (format, ...) ((void)0) um Fallen wie zu vermeiden if(cond) TRACE(...) ?
– Gregor
17. Juli 2014 um 15:06 Uhr
@Gregory: Was ist die “Falle”? Wenn Sie nach dem Makroaufruf kein Semikolon gesetzt haben, ist es wahrscheinlich sowieso falsch – if(x) TRACE("y = %d", y ); würde sich dazu entschließen if(x); was keine Wirkung haben wird.
– Clifford
17. Juli 2014 um 15:38 Uhr
Meine schlechte … Ich war mir ziemlich sicher, dass ich irgendwann einen Compiler dafür benutzt habe ; war keine gültige Aussage, aber es ist kein Problem für GCC. assert.h scheint immer noch zu verwenden #define assert(ignore)((void) 0) obwohl.
– Gregor
18. Juli 2014 um 15:44 Uhr
Das liegt daran, dass der C-Standard dies vorschreibt assert zu einem void-Ausdruck erweitern, unabhängig vom Wert von NDEBUG
– Gabriel Ravier
30. Juli 2021 um 15:08 Uhr
Foreach-Schleife für GCC, insbesondere C99 mit GNU-Erweiterungen. Funktioniert mit Strings und Arrays. Dynamisch zugewiesene Arrays können verwendet werden, indem sie in einen Zeiger auf ein Array umgewandelt und dann dereferenziert werden.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define FOREACH_COMP(INDEX, ARRAY, ARRAY_TYPE, SIZE) \
__extension__ \
({ \
bool ret = 0; \
if (__builtin_types_compatible_p (const char*, ARRAY_TYPE)) \
ret = INDEX < strlen ((const char*)ARRAY); \
else \
ret = INDEX < SIZE; \
ret; \
})
#define FOREACH_ELEM(INDEX, ARRAY, TYPE) \
__extension__ \
({ \
TYPE *tmp_array_ = ARRAY; \
&tmp_array_[INDEX]; \
})
#define FOREACH(VAR, ARRAY) \
for (void *array_ = (void*)(ARRAY); array_; array_ = 0) \
for (size_t i_ = 0; i_ && array_ && FOREACH_COMP (i_, array_, \
__typeof__ (ARRAY), \
sizeof (ARRAY) / sizeof ((ARRAY)[0])); \
i_++) \
for (bool b_ = 1; b_; (b_) ? array_ = 0 : 0, b_ = 0) \
for (VAR = FOREACH_ELEM (i_, array_, __typeof__ ((ARRAY)[0])); b_; b_ = 0)
/* example's */
int
main (int argc, char **argv)
{
int array[10];
/* initialize the array */
int i = 0;
FOREACH (int *x, array)
{
*x = i;
++i;
}
char *str = "hello, world!";
FOREACH (char *c, str)
printf ("%c\n", *c);
/* Use a cast for dynamically allocated arrays */
int *dynamic = malloc (sizeof (int) * 10);
for (int i = 0; i < 10; i++)
dynamic[i] = i;
FOREACH (int *i, *(int(*)[10])(dynamic))
printf ("%d\n", *i);
return EXIT_SUCCESS;
}
Dieser Code wurde getestet, um mit GCC, ICC und Clang unter GNU/Linux zu funktionieren.
Lambda-Ausdrücke (nur GCC)
#define lambda(return_type, ...) \
__extension__ \
({ \
return_type __fn__ __VA_ARGS__ \
__fn__; \
})
int
main (int argc, char **argv)
{
int (*max) (int, int) =
lambda (int, (int x, int y) { return x > y ? x : y; });
return max (1, 2);
}
Sollte nicht der NDEBUG sein #define TRACE (format, ...) ((void)0) um Fallen wie zu vermeiden if(cond) TRACE(...) ?
– Gregor
17. Juli 2014 um 15:06 Uhr
@Gregory: Was ist die “Falle”? Wenn Sie nach dem Makroaufruf kein Semikolon gesetzt haben, ist es wahrscheinlich sowieso falsch – if(x) TRACE("y = %d", y ); würde sich dazu entschließen if(x); was keine Wirkung haben wird.
– Clifford
17. Juli 2014 um 15:38 Uhr
Meine schlechte … Ich war mir ziemlich sicher, dass ich irgendwann einen Compiler dafür benutzt habe ; war keine gültige Aussage, aber es ist kein Problem für GCC. assert.h scheint immer noch zu verwenden #define assert(ignore)((void) 0) obwohl.
– Gregor
18. Juli 2014 um 15:44 Uhr
Das liegt daran, dass der C-Standard dies vorschreibt assert zu einem void-Ausdruck erweitern, unabhängig vom Wert von NDEBUG
– Gabriel Ravier
30. Juli 2021 um 15:08 Uhr
Gemeinschaft
Jemand anderes erwähnte container_of(), lieferte aber keine Erklärung für dieses wirklich praktische Makro. Nehmen wir an, Sie haben eine Struktur, die so aussieht:
struct thing {
int a;
int b;
};
Nun, wenn wir einen Zeiger auf haben bwir können benutzen container_of() um einen Zeiger zu bekommen Ding typsicher:
Dies ist nützlich, um abstrakte Datenstrukturen zu erstellen. Anstatt beispielsweise den Ansatz zu verwenden, den queue.h zum Erstellen von Dingen wie SLIST (Tonnen verrückter Makros für jede Operation) verwendet, können Sie jetzt eine Slist-Implementierung schreiben, die ungefähr so aussieht:
Was kein verrückter Makrocode ist. Es gibt gute Compiler-Zeilennummern bei Fehlern und funktioniert gut mit dem Debugger. Es ist auch ziemlich typsicher, außer in Fällen, in denen Strukturen mehrere Typen verwenden (z. B. wenn wir es erlaubt haben Strukturfarbe im folgenden Beispiel, um auf mehr verknüpften Listen als nur der zu sein Farben eines).
Benutzer können Ihre Bibliothek jetzt wie folgt verwenden:
struct colors {
int r;
int g;
int b;
struct slist_el colors;
};
struct *color = malloc(sizeof(struct person));
color->r = 255;
color->g = 0;
color->b = 0;
slist_insert_head(color_stack, &color->colors);
...
el = slist_pop_head(color_stack);
color = el == NULL ? NULL : container_of(el, struct color, colors);
13840400cookie-checkDie nützlichsten benutzerdefinierten C-Makros (in GCC, auch C99)? [closed]yes
Und Sie implementieren hier keine der Standardformen der Vektormultiplikation (weder Skalarprodukt, Kreuzprodukt noch Tensorprodukt).
– David Thornley
20. November 2009 um 18:07 Uhr
Die komponentenweise Multiplikation hat ihren Nutzen, aber die durch das Makro auferlegte Dimensionsbegrenzung macht sie nicht so nützlich.
– aussen
20. November 2009 um 18:17 Uhr
@outis: Dies ist für die Verwendung in Bereichen vorgesehen, in denen die Abmessungsgrenze aufgrund der Art des Anwendungsbereichs streng festgelegt ist. Wie zum Beispiel in 3D-Grafiken.
– AnT steht zu Russland
20. November 2009 um 18:30 Uhr
Aber angenommen, die Anzahl der Dimensionen im Universum ändert sich, der Code wird brechen – das ist die Art von Kurzsichtigkeit, die zum Jahr-2000-Problem geführt hat.
– Martin Beckett
20. November 2009 um 18:41 Uhr
Ja … Es ist wie dieses berühmte Zitat aus dem Xerox-Handbuch. Einer der Vorteile der Deklaration von PI als benannte Konstante besteht darin, dass Sie den Code anpassen können, falls sich der Wert von Pi ändert.
– AnT steht zu Russland
20. November 2009 um 19:09 Uhr