Gute Programmierpraxis für Makrodefinitionen (#define) in C [closed]
Lesezeit: 4 Minuten
Srikanth
Definieren Sie zum Beispiel niemals ein Makro wie dieses:
#define DANGER 60 + 2
Dies kann potenziell gefährlich sein, wenn wir eine Operation wie diese durchführen:
int wrong_value = DANGER * 2; // Expecting 124
Definieren Sie stattdessen so, weil Sie nicht wissen, wie der Benutzer des Makros es verwenden könnte:
#define HARMLESS (60 + 2)
Das Beispiel ist trivial, aber das erklärt ziemlich genau meine Frage. Gibt es eine Reihe von Richtlinien oder Best Practices, die Sie beim Schreiben eines Makros empfehlen würden?
Vielen Dank für Ihre Zeit!
Das ist keine Frage. Es ist eine Aussage.
– BIBD
26. November 2008 um 15:42 Uhr
Roddy
Sie sollten nicht nur die Argumente in Klammern setzen, sondern auch den zurückgegebenen Ausdruck.
#define MIN(a,b) a < b ? a : b // WRONG
int i = MIN(1,2); // works
int i = MIN(1,1+1); // breaks
#define MIN(a,b) (a) < (b) ? (a) : (b) // STILL WRONG
int i = MIN(1,2); // works
int i = MIN(1,1+1); // now works
int i = MIN(1,2) + 1; // breaks
#define MIN(a,b) ((a) < (b) ? (a) : (b)) // GOOD
int i = MIN(1,2); // works
int i = MIN(1,1+1); // now works
int i = MIN(1,2) + 1; // works
Jedoch, MIN(3,i++) ist noch kaputt…
Die beste Regel ist #defines nur zu verwenden, wenn KEIN ANDERER ANSATZ FUNKTIONIERT! Ich weiß, dass Sie nach C statt C++ fragen, aber denken Sie trotzdem an ihn.
Vielleicht sollten Sie einen Hinweis zur Verwendung von enum hinzufügen, anstatt numerische Konstanten mit #define zu definieren.
– Jonathan Leffler
26. November 2008 um 17:53 Uhr
Ach! MIN(3, i++)! Ich habe diesen Fall nie in Betracht gezogen … Das muss scheiße sein, das zu debuggen!
– Adam Davis
26. November 2008 um 18:42 Uhr
Natürlich ist die Lösung trivial – machen Sie eine Funktion daraus oder entfernen Sie das Inkrement aus dieser Codezeile. Ich stimme nicht zu, dass Makros nur dann verwendet werden sollten, wenn kein anderer Ansatz funktioniert – Wie jedes andere Tool können Makros bei unsachgemäßer Verwendung gefährlich sein, aber gut gemacht können sie die Entwicklungszeit verkürzen
– Adam Davis
26. November 2008 um 18:45 Uhr
Wenn Sie ein Makro ausführen, das sein Argument ausführen und sich wie ein Ausdruck verhalten soll, ist dies idiomatisch:
#define DOIT(x) do { x } while(0)
Dieses Formular hat folgende Vorteile:
Es braucht ein abschließendes Semikolon
Es funktioniert mit Verschachtelungen und geschweiften Klammern, zB mit if/else
Und was wäre wenn x ist nur eine einzige Aussage? Zum Beispiel foobar = 42müssen wir es noch einschließen do{}while(0)oder reicht es aus, es einfach in Klammern zu setzen?
– Denilson Sá Maia
3. September 2011 um 4:10 Uhr
Sie würden es immer noch brauchen, wenn Sie 1) das Makro wollen fordern ein abschließendes Semikolon und 2) es als eigenständige Anweisung in einer einzeiligen if-Anweisung zu verwenden.
Verwenden Sie Klammern um das gesamte Makro und um jedes Argument, auf das in der Erweiterungsliste verwiesen wird:
#define MAX(x, y) ((x) > (y) ? (x) : (y))
Vermeiden Sie es, Makros zu schreiben, die ihre Argumente mehrmals auswerten. Solche Makros verhalten sich nicht wie erwartet, wenn Argumente Nebenwirkungen haben:
MAX(a++, b);
Werde auswerten a++ zweimal wenn a ist größer als b.
Verwenden Sie GROSSBUCHSTABEN für Makros, um deutlich zu machen, dass es sich um ein Makro und nicht um eine Funktion handelt, damit die Unterschiede entsprechend berücksichtigt werden können (eine weitere allgemeine bewährte Vorgehensweise besteht darin, auch keine Argumente mit Nebeneffekten an Funktionen zu übergeben).
Verwenden Sie keine Makros, um Typen wie folgt umzubenennen:
#define pint int *
weil es sich nicht wie erwartet verhält, wenn jemand tippt
pint a, b;
Verwenden Sie stattdessen typedefs.
Verwenden Sie statische konstante Werte anstelle von Makros für konstante Werte, ganzzahlige oder andere. Der Compiler kann sie oft wegoptimieren, und sie bleiben ein Bürger erster Klasse im Typsystem der Sprache.
static const int DANGER = 60 + 2;
Setzen Sie in der Erweiterung Klammern um die Argumente, damit Sie das beabsichtigte Verhalten erhalten, wenn sie einen Ausdruck übergeben.
#define LESS_THAN(X,Y) (((X) < (Y) ? (X) : (Y))
Ironischerweise ist dies ein schlechtes Makro, da es seine Argumente zweimal auswertet.
Ironischerweise ist dies ein schlechtes Makro, da es seine Argumente zweimal auswertet.
– Benutzer4945014
19. November 2021 um 17:44 Uhr
Verwenden Sie ziemlich eindeutige Namen für Ihre Makros, da sie einen globalen Geltungsbereich haben und mit allem kollidieren können, also:
#define MAX 10
könnte leicht mit anderem Code kollidieren, also:
#define MYPROJECT_MAX 10
oder etwas noch Einzigartigeres wäre besser.
Ich habe Fälle gesehen, in denen ein Konflikt dieser Art keinen Kompilierungsfehler, sondern leicht falschen Code erzeugt hat, sodass er ziemlich heimtückisch sein kann.
13449800cookie-checkGute Programmierpraxis für Makrodefinitionen (#define) in C [closed]yes
Das ist keine Frage. Es ist eine Aussage.
– BIBD
26. November 2008 um 15:42 Uhr