Gute Programmierpraxis für Makrodefinitionen (#define) in C [closed]

Lesezeit: 4 Minuten

Benutzer-Avatar
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

Benutzer-Avatar
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:

  1. Es braucht ein abschließendes Semikolon
  2. 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.

    – luis.espinal

    5. November 2011 um 20:46 Uhr

  • Überprüfen Sie diesen Link. Es erklärt es sehr gut: kernelnewbies.org/FAQ/DoWhile0

    – luis.espinal

    5. November 2011 um 20:47 Uhr

Benutzer-Avatar
Robert Glück

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.

    Benutzer4945014

    19. November 2021 um 17:44 Uhr

Benutzer-Avatar
dalle

Antwort auf die MAX/MIN-Makros, entnommen aus GCC-Hacks im Linux-Kernel:

#define min(x, y) ({                       \
        typeof(x) _min1 = (x);             \
        typeof(y) _min2 = (y);             \
        (void) (&_min1 == &_min2);         \
        _min1 < _min2 ? _min1 : _min2; })

  • 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.

1344980cookie-checkGute Programmierpraxis für Makrodefinitionen (#define) in C [closed]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy