Domanda

Cosa C macro è secondo lei è la più utile? Ho trovato la seguente, che io uso per fare vettore aritmetica in 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];}

Funziona così:

v3_op_v3(vectorA, +, vectorB, vectorC);
v3_op_v3(vectorE, *, vectorF, vectorJ);
...
È stato utile?

Soluzione

for-each loop 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);
}

Altri suggerimenti

#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)

E, naturalmente, vari MIN, MAX, ABS ecc.

Nota, BTW, che nessuno dei precedenti può essere implementata da una funzione in C.

P.S. Io probabilmente individuare la macro IMPLIES di cui sopra come uno di quelli più utili. Il suo scopo principale è quello di facilitare la scrittura di asserzioni più elegante e leggibile, come in

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

Il punto chiave con C macro è quello di utilizzare in modo corretto. Nella mia mente ci sono tre categorie (non considerando il loro utilizzo solo per dare nomi descrittivi alle costanti)

  1. Come scorciatoia per pezzo di codici non si vuole ripetere
  2. Fornire una funzione di uso generale
  3. Modificare la struttura del linguaggio C (a quanto pare)

Nel primo caso, la macro vivrà solo all'interno del programma (di solito solo un file) in modo da poter utilizzare le macro come quello che avete pubblicato che non è protetto contro la doppia valutazione dei parametri e utilizza {...}; (potenzialmente pericolosi!) .

Nel secondo caso (e ancora di più nel terzo) è necessario essere estremamente attenti che le macro si comportano correttamente come se fossero reali costrutti C.

La macro hai postato da GCC (min e max) è un esempio di questo, usano le variabili globali _a e _b per evitare il rischio di doppia valutazione (come in max(x++,y++)) (beh, che utilizzano le estensioni del GCC, ma il concetto è lo stesso).

Mi piace usare le macro in cui contribuisce a rendere le cose più chiare, ma sono uno strumento tagliente! Probabilmente è quello che ha dato loro una cattiva reputazione, penso che siano uno strumento molto utile e C sarebbero state molto più povera se non fossero presenti.

Vedo che altri hanno fornito esempi del punto 2 (macro come funzioni), lasciatemi fare un esempio di creazione di un nuovo costrutto C: la macchina a stati finiti. (Ho già postato questo su SO, ma non riesco a essere in grado di trovarlo)

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

che si utilizza in questo modo:

 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 */
 } 

È possibile aggiungere variazione su questo tema per ottenere il sapore della FSM è necessario.

Qualcuno può non piacere questo esempio, ma lo trovo perfetto per dimostrare come le macro semplice in grado di rendere il codice più leggibile ed espressivo.

Se è necessario definire i dati più volte in diversi contesti, le macro possono aiutare a evitare dovete rimettere in vendita la stessa cosa più volte.

Per esempio, diciamo che si desidera definire un enum di colori e una funzione enum a stringa, piuttosto che elencare tutti i colori per due volte, è possibile creare un file dei colori ( colors.def ):

c(red)
c(blue)
c(green)
c(yellow)
c(brown)

Ora è possibile nel file c è possibile definire l'enum e la vostra funzione di conversione stringa:

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

Si noti che la mancanza di una virgola tra "%s::%s(%d)" e format è intenzionale. Si stampa una stringa formattata con posizione di origine anteposta. Io lavoro in sistemi embedded real-time così spesso includo anche un timestamp in uscita pure.

ciclo foreach per GCC, in particolare C99 con GNU estensioni. Funziona con stringhe e array. Dinamicamente array assegnati possono essere utilizzati gettandoli a un puntatore a una matrice, e quindi li dereferencing.

#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;
}

Questo codice è stato testato per funzionare con GCC, ICC e Clang su GNU / Linux.

espressioni lambda (GCC solo)

#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);
}

Salva te stesso qualche errore di conteggio incline

Qualcun altro ha menzionato container_of () , ma non ha fornito una spiegazione per questo veramente utile macro. Diciamo che si dispone di una struttura che assomiglia a questo:

struct thing {
    int a;
    int b;
};

Ora, se abbiamo un puntatore a b , possiamo usare container_of () per ottenere un puntatore a cosa in un tipo di moda di sicurezza :

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

Questo è utile per creare strutture dati astratte. Ad esempio, invece di prendere l'approccio queue.h prende per la creazione di cose come SLIST (tonnellate di macro pazzi per ogni operazione), ora è possibile scrivere un'implementazione slist che sembra qualcosa di simile:

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);   
}

Il che non è pazzo codice di macro. Esso darà buon compilatore line-numeri su errori e lavora bello con il debugger. E 'anche abbastanza typesafe, fatta eccezione per i casi in cui le strutture utilizzano più tipi (ad esempio se permettessimo colore struct nell'esempio qui sotto per essere sulle liste più legate che solo i colori uno) .

Gli utenti possono ora utilizzare la libreria in questo modo:

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);

Questo è da kernel Linux (specifico gcc):

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

Un altro manca da altre risposte:

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

mi piace anche questo:

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

E come macro-haters fare paragoni virgola mobile giusto?

A soli quelli standard:

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

, ma non c'è niente di troppo spiffy lì.

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

Trova il 32bit intero senza segno più vicino che è più grande di x. Uso questo per raddoppiare la dimensione degli array (ossia il livello massimo dell'acqua).

byte Pack, parole, DWORD in parole, DWORD e QWords:

#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) 

argomenti mettere tra parentesi è sempre una buona pratica per evitare effetti collaterali sulla espansione.

anche multi-tipo minimo e massimo del genere

//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; })

Verifica se un punto floating x non è un numero:

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

Uno (dei pochissimi) che uso regolarmente è una macro per dichiarare un argomento o una variabile come inutilizzato. La soluzione più compatibile notare questo (IMHO) varia a seconda del compilatore.

Questo è impressionante:

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

E lo uso come:

object = NEW(object_type, 1);

TRUE e FALSE sembrano essere popolare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top