Самые полезные C-макросы, созданные пользователем (в GCC также C99)?[закрыто]

StackOverflow https://stackoverflow.com/questions/1772119

  •  21-09-2019
  •  | 
  •  

Вопрос

Что С макрос по вашему мнению самый полезный?Я нашел следующий, который я использую для векторной арифметики в С:

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

Это работает следующим образом:

v3_op_v3(vectorA, +, vectorB, vectorC);
v3_op_v3(vectorE, *, vectorF, vectorJ);
...
Это было полезно?

Решение

за свой цикл в 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);
}

Другие советы

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

И, конечно же, различные мин, макс, абс и т. Д.

Обратите внимание, кстати, что ни одно из вышеперечисленных не может быть реализовано функцией в C.

PS я бы, наверное, вычеркнул выше IMPLIES Макро как один из самых полезных. Его главная цель - облегчить написание более элегантных и читаемых утверждений, как в

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

Ключевым моментом макросов C является их правильное использование.На мой взгляд, есть три категории (не считая их использования только для присвоения описательных имен константам)

  1. В качестве сокращения фрагментов кода, которые не хочется повторять.
  2. Обеспечить функцию общего использования
  3. Изменить структуру языка C (видимо)

В первом случае ваш макрос будет жить внутри вашей программы (обычно это просто файл), поэтому вы можете использовать макросы, подобные тому, который вы опубликовали, который не защищен от двойной оценки параметров и использования. {...}; (потенциально опасно!).

Во втором случае (и тем более в третьем) вам нужно быть очень сильно будьте осторожны, чтобы ваши макросы вели себя правильно, как если бы они были настоящими конструкциями C.

Макрос, который вы опубликовали из GCC (мин и макс), является примером этого, они используют глобальные переменные. _a и _b чтобы избежать риска двойной оценки (как в max(x++,y++)) (ну, они используют расширения GCC, но концепция та же).

Мне нравится использовать макросы, они помогают прояснить ситуацию, но это очень острый инструмент!Вероятно, именно это и дало им такую ​​плохую репутацию. Я считаю, что это очень полезный инструмент, и если бы их не было, C был бы намного хуже.

Я вижу, что другие привели примеры пункта 2 (макросы как функции), позвольте мне привести пример создания новой конструкции C:Конечный автомат.(Я уже публиковал это на SO, но, похоже, не могу его найти)

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

что вы используете таким образом:

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

Вы можете добавить вариации на эту тему, чтобы получить тот вариант FSM, который вам нужен.

Кому-то этот пример может не понравиться, но я считаю, что он идеально подходит для демонстрации того, как простые макросы могут сделать ваш код более разборчивым и выразительным.

Если вам нужно несколько раз определять данные в разных контекстах, макросы могут помочь вам избежать, чтобы привести к тому, что он несколько раз переиздает одно и то же.

Например, допустим, что вы хотите определить перечисление цветов и функцию перечисления к строке, а затем дважды перечислите все цвета, вы можете создать файл цветов (Colors.def):

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

Теперь вы можете в своем файле C, вы можете определить свой перечисление и функцию преобразования строки:

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

Обратите внимание, что отсутствие запятой между "%s::%s(%d)" а также format преднамеренный. Он печатает форматированную строку с приготовлением местоположения источника. Я так часто работаю в встроенных системах в режиме реального времени, я также включаю метку времени в результате.

Foreach Loop для GCC, в частности C99 с расширениями GNU. Работает с струнами и массивами. Динамически распределенные массивы могут быть использованы, подчиняя их к указателю на массив, а затем смягчив их.

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

Этот код был протестирован для работы с GCC, ICC и Clang на GNU/Linux.

Lambda выражения (только 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);
}

Сохраните себя некоторым склоном к ошибкам подсчета

Кто -то еще упомянул container_of (), но не дал объяснения для этого действительно удобного макроса. Допустим, у вас есть структура, которая выглядит так:

struct thing {
    int a;
    int b;
};

Теперь, если у нас есть указатель на беременный, мы можем использовать container_of () Чтобы получить указатель на вещь в безопасном виде типа:

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

Это полезно при создании абстрактных структур данных. Например, вместо того, чтобы принимать очередь подходов. H берет на себя для создания таких вещей, как Slast (тонны сумасшедших макросов для каждой операции), теперь вы можете написать реализацию строк, которая выглядит примерно так:

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

Который не сумасшедший макрод. Он даст хорошие компиляторы линейные номера по ошибкам и хорошо работает с отладчиком. Это также довольно typesafe, за исключением случаев, когда структуры используют несколько типов (например, если мы допустили Структурный цвет в приведенном ниже примере быть в более связанных списках, чем просто цвета один).

Пользователи теперь могут использовать вашу библиотеку так:

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

Это из ядра Linux (специфик для GCC):

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

Еще один отсутствует в других ответах:

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

Мне также нравится этот:

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

А как вы, макрос, проводят справедливые сравнения с плавающей запятой?

Просто стандартные:

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

Но там нет ничего слишком резкого.

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

Найдите ближайшее 32 -битное целое число без знака, которое больше x. Я использую это, чтобы удвоить размер массивов (то есть высокая отметка).

Упаковать байты, слова, DWORDS в слова, DWORDS и 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) 

Сложные в скобках аргументы. Всегда хорошая практика, чтобы избежать побочных эффектов на расширение.

Также как много типа минимального и максимального

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

Проверка, будь то плавающая точка Икс это не число:

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

Один (из тех немногих), которые я использую регулярно, - это макрос, чтобы объявить аргумент или переменную как неиспользованную. Наиболее совместимое решение, чтобы отметить это (IMHO) варьируется в результате компилятора.

Это потрясающее:

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

И я использую это как:

object = NEW(object_type, 1);

TRUE and FALSE seem to be popular.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top