Frage

Was C Makro ist Ihrer Meinung nach am nützlichsten? Ich habe das folgende gefunden, in dem ich Vektorarithmetik aus mache C:

#define v3_op_v3(x, op, y, z) {z[0]=x[0] op y[0]; \
                               z[1]=x[1] op y[1]; \
                               z[2]=x[2] op y[2];}

Es funktioniert so:

v3_op_v3(vectorA, +, vectorB, vectorC);
v3_op_v3(vectorE, *, vectorF, vectorJ);
...
War es hilfreich?

Lösung

For-Each-Schleife in C99:

#define foreach(item, array) \
    for(int keep=1, \
            count=0,\
            size=sizeof (array)/sizeof *(array); \
        keep && count != size; \
        keep = !keep, count++) \
      for(item = (array)+count; keep; keep = !keep)

int main() {
  int a[] = { 1, 2, 3 };
  int sum = 0;
  foreach(int const* c, a)
    sum += *c;
  printf("sum = %d\n", sum);

  // multi-dim array
  int a1[][2] = { { 1, 2 }, { 3, 4 } };
  foreach(int (*c1)[2], a1)
    foreach(int *c2, *c1) 
      printf("c2 = %d\n", *c2);
}

Andere Tipps

#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 verschiedene Min, max, ABS usw.

Hinweis, übrigens, dass keines der oben genannten durch eine Funktion in C implementiert werden kann.

PS Ich würde wahrscheinlich das oben genannte herausholen IMPLIES Makro als einer der nützlichsten. Sein Hauptzweck ist es, das Schreiben eleganterer und lesbarerer Behauptungen zu erleichtern, wie in

void foo(int array[], int n) {
  assert(IMPLIES(n > 0, array != NULL));
  ...

Der Schlüsselpunkt bei C -Makros besteht darin, sie richtig zu verwenden. In meinen Augen gibt es drei Kategorien (nicht in Betracht ziehen, sie zu verwenden, um Konstanten beschreibende Namen zu geben)

  1. Als Abkürzung für ein Stück Codes möchte man sich nicht wiederholen
  2. Geben Sie eine allgemeine Nutzungsfunktion an
  3. Ändern Sie die Struktur der C -Sprache (anscheinend)

Im ersten Fall lebt Ihr Makro nur in Ihrem Programm (normalerweise nur eine Datei) {...}; (potenziell gefährlich!).

Im zweiten Fall (und noch mehr im dritten) müssen Sie sein äußerst Achten Sie darauf, dass sich Ihre Makros richtig verhalten, als wären sie echte C -Konstrukte.

Das von GCC (Min und Max) gepostete Makro ist ein Beispiel dafür. Sie verwenden die globalen Variablen _a und _b Um das Risiko einer doppelten Bewertung zu vermeiden (wie in max(x++,y++)) (Nun, sie verwenden GCC -Erweiterungen, aber das Konzept ist das gleiche).

Ich mag es, Makros zu verwenden, bei denen es hilft, die Dinge klarer zu machen, aber sie sind ein scharfes Werkzeug! Wahrscheinlich hat das ihnen einen so schlechten Ruf gegeben, ich denke, sie sind ein sehr nützliches Werkzeug und C wäre viel ärmer gewesen, wenn sie nicht anwesend wären.

Ich sehe, andere haben Beispiele für Punkt 2 (Makros als Funktionen) geliefert. Lassen Sie mich ein Beispiel für die Erstellung eines neuen C -Konstrukts geben: die endliche Zustandsmaschine. (Ich habe das schon so gepostet, aber ich kann es nicht in der Lage sein, es zu finden)

 #define FSM            for(;;)
 #define STATE(x)       x##_s 
 #define NEXTSTATE(x)   goto x##_s

dass Sie so verwenden:

 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 dieses Thema Variation hinzufügen, um den Aroma von FSM zu erhalten, den Sie benötigen.

Jemand mag dieses Beispiel nicht mögen, aber ich finde es perfekt, um zu demonstrieren, wie einfach Makros Ihren Code lesbarer und ausdrucksvoller machen können.

Wenn Sie Daten mehrmals in verschiedenen Kontexten definieren müssen, können Makros Ihnen dabei helfen, dasselbe mehrmals neu aufzubauen.

Nehmen wir beispielsweise an, Sie möchten einen Auflauf von Farben und eine Enum-to-String-Funktion definieren, anstatt alle Farben zweimal aufzulisten, Sie können eine Datei der Farben erstellen (Farben (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 Conversion -Funktion 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]);
};
#if defined NDEBUG
    #define TRACE( format, ... )
#else
    #define TRACE( format, ... )   printf( "%s::%s(%d)" format, __FILE__, __FUNCTION__,  __LINE__, __VA_ARGS__ )
#endif

Beachten Sie, dass das Fehlen eines Kommas zwischen "%s::%s(%d)" und format ist absichtlich. Es druckt eine formatierte Zeichenfolge mit vorgeschriebener Quellesposition. Ich arbeite in Echtzeit-eingebetteten Systemen, so dass ich auch einen Zeitstempel in die Ausgabe einfüge.

Foreach -Schleife für GCC, insbesondere C99 mit GNU -Erweiterungen. Arbeitet mit Saiten und Arrays. Dynamisch zugewiesene Arrays können verwendet werden, indem sie an einen Zeiger auf ein Array geworfen und sie dann derenferenzieren.

#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 auf GNU/Linux zu arbeiten.

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);
}
#define COLUMNS(S,E) [ (E) - (S) + 1 ]


struct 
{
    char firstName COLUMNS ( 1, 20);
    char LastName  COLUMNS (21, 40);
    char ssn       COLUMNS (41, 49);
}

Speichern Sie sich ein Fehler, das anfällig für Fehler ist

Jemand anderes erwähnt container_of (), lieferte aber keine Erklärung für dieses sehr praktische Makro. Nehmen wir an, Sie haben eine Struktur, die so aussieht:

struct thing {
    int a;
    int b;
};

Nun, wenn wir einen Zeiger haben b, wir können benutzen container_of () einen Zeiger aufnehmen Ding auf eine Art sichere Art und Weise:

int *bp = ...;
struct thing *t = container_of(bp, struct thing, b);

Dies ist nützlich bei der Erstellung abstrakter Datenstrukturen. Anstatt die Annäherungswarteschlange zu nehmen, nimmt er zum Beispiel, um Dinge wie Slist (Tonnen von verrückten Makros für jede Operation) zu erstellen, jetzt eine Slist -Implementierung, die so etwas aussieht:

struct slist_el {
    struct slist_el *next;
};

struct slist_head {
    struct slist_el *first;
};

void
slist_insert_head(struct slist_head *head, struct slist_el *el)
{
    el->next = head->first;
    head->first = el;
}

struct slist_el
slist_pop_head(struct slist_head *head)
{
    struct slist_el *el;

    if (head->first == NULL)
        return NULL;

    el = head->first;
    head->first = el->next;
    return (el);   
}

Das ist kein verrückter Makrocode. Es wird gute Compiler-Zahlen für Fehler geben und funktioniert gut mit dem Debugger. Es ist auch fair typesafe, mit Ausnahme von Fällen, in denen Strukturen mehrere Typen verwenden (z. B. wenn wir zulässig sind Strukturfarbe im folgenden Beispiel, um in mehr verknüpften Listen zu sein als nur die Farben eines).

Benutzer können jetzt Ihre Bibliothek 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);

Dieser stammt aus Linux -Kernel (GCC -Spezifikum):

#define container_of(ptr, type, member) ({                  \
const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) ); })

Ein weiterer vermisst in anderen Antworten:

#define LSB(x) ((x) ^ ((x) - 1) & (x))   // least significant bit

Ich mag auch das:

#define COMPARE_FLOATS(a,b,epsilon) (fabs(a - b) <= epsilon * fabs(a))

Und wie machen Sie Makros-Hasser faire Gleitkomma-Vergleiche?

Nur die Standards:

#define LENGTH(array) (sizeof(array) / sizeof (array[0]))
#define QUOTE(name) #name
#define STR(name) QUOTE(name)

Aber dort gibt es nichts zu spiffig.

#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))

Finden Sie die am nächsten gelegene 32 -Bit -Ganzzahl, die größer als x ist. Ich benutze dies, um die Größe der Arrays zu verdoppeln (dh der Hochwassermarke).

Packen Sie Bytes, Wörter, Dwords in Wörter, Dwords und QWords ein:

#define ULONGLONG unsigned __int64
#define MAKEWORD(h,l) ((unsigned short) ((h) << 8)) | (l)
#define MAKEDWORD(h,l) ((DWORD) ((h) << 16)) | (l)
#define MAKEQWORD(h,l) ((ULONGLONG)((h) << 32)) | (l) 

Klammern Argumente Es ist immer eine gute Praxis, Nebenwirkungen auf die Erweiterung zu vermeiden.

Auch minimales und maximales Multi-Typ-Typ

//NOTE: GCC extension !
#define max(a,b) ({typeof (a) _a=(a); typeof (b) _b=(b); _a > _b ? _a:_b; })
#define min(a,b) ({typeof (a) _a=(a); typeof (b) _b=(b); _a < _b ? _a:_b; })

Überprüfen Sie, ob ein schwimmender Punkt x ist keine Nummer:

#define ISNAN(x) ((x) != (x))

Eine (von wenigen), die ich regelmäßig verwende, ist ein Makro, um ein Argument oder eine Variable als nicht genutzt zu erklären. Die am besten kompatibele Lösung, um diese (IMHO) zu beachten, variiert je nach Compiler.

Dieser ist großartig:

#define NEW(type, n) ( (type *) malloc(1 + (n) * sizeof(type)) )

Und ich benutze es wie:

object = NEW(object_type, 1);

Wahr und falsch scheinen beliebt zu sein.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top