Подтвердить что ты не робот

Зачем использовать макросы в C?

Possible Duplicate:
What are C macros useful for?

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

TEST(a,%d);
#define TEST(a,b) printf(" The value of " #a " = " #b " \n", a)

//which expands to 
printf(" The value of a = %d \n",a);

(пример из здесь)

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

Так какие полезные вещи могут сделать макросы для вас?

4b9b3361

Ответ 1

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

#define MAX(x,y) ((x) > (y) ? (x) : (y))

Этот макрос удовлетворяет целым числам, удвоениям, поплавкам и т.д.

Ответ 2

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

Одной из причин использования макросов является производительность. Это способ устранения служебных издержек вызова функций, поскольку они всегда расширяются в строке, в отличие от ключевого слова inline, которое часто игнорируется подсказкой для компилятора и даже не существует (в стандарте) до C99. Например, см. Семейство макросов FD_, используемых в сочетании с fd_sets, используемыми select и pselect. Эти fd_sets на самом деле являются просто битами, а макросы FD_ скрывают операции свертывания бит. Было бы очень неприятно выписывать бит, который каждый раз наводил на себя, и вызов функции был бы большим количеством накладных расходов для такой быстрой операции, если бы она не была встроена.

Кроме того, макросы могут выполнять некоторые функции, которые не могут выполнять функции. Рассмотрим вставку маркера. Поскольку препроцессор работает перед компилятором, он может создавать новые идентификаторы для компилятора. Это может дать вам сокращенный способ создания множества похожих определений, например.

#define DEF_PAIR_OF(dtype) \
  typedef struct pair_of_##dtype { \
       dtype first; \
       dtype second; \
  } pair_of_##dtype##_t 

 DEF_PAIR_OF(int);
 DEF_PAIR_OF(double);
 DEF_PAIR_OF(MyStruct);
 /* etc */

Другое дело, что функция не может превратить информацию о времени компиляции в информацию о времени выполнения:

#ifdef DEBUG
#define REPORT_PTR_VALUE(v) printf("Pointer %s points to %p\n", #v, v)
#else 
#define REPORT_PTR_VALUE(v)
#endif

void someFunction(const int* reallyCoolPointer) {
  REPORT_PTR_VALUE(reallyCoolPointer);
  /* Other code */
}

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

Ответ 3

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

Ответ 4

Иногда вы хотите выполнять ведение журнала, но только в режиме отладки. Итак, вы пишете что-то вроде:

#ifdef DEBUG
#define LOG_MSG(x) printf(x);
#else
#define LOG_MSG(X)
#endif

Иногда вы просто хотите что-то отключить или включить на основе переключателя времени компиляции. Например, моя компания делает некоторый совлокальный брендинг нашего продукта, и партнеры просят отключить все. Итак, мы сделаем что-то вроде:

#ifndef SPECIAL_PARTNER_BUILD
    DoSomethingReallyAwesome();
#endif

Затем создайте с -DSPECIAL_PARTNER_BUILD, когда даете партнеру капли.

Среди многих других возможных причин.

Иногда вы просто хотите сохранить ввод текста для вещей, которые вы делаете снова и снова. Некоторые люди принимают это далеко (кашель MFC кашель) и пишут, что составляет их собственный язык в Макросах, чтобы абстрагироваться от сложного API. Это отлаживает кошмар фриккина.

Ответ 5

Макросы могут иметь много разных применений, кроме функций, подобных вещам.

Очень полезно использовать макросы для любого магического числа или связанной строки.

#define MY_MAGIC_NUM 57

/*MY_MAGIC_NUM used all through out the code*/

Ответ 6

Макросы C могут генерировать код во время компиляции. Это может (ab) использоваться для эффективного создания предметно-ориентированных языков с новыми ключевыми словами и поведением.

Один из моих любимых примеров - метод Саймона Татхамса по реализации сопрограмм в Си через макросы. Простейший макрос реализован так:

#define crBegin static int state=0; switch(state) { case 0:

Да, с непревзойденной скобкой. Другие макросы исправят это.

Ответ 7

Мы использовали этот тип макроса в нашем коде:

// convenience macros for implementing field setter/getter functions
#ifndef CREATE_GET_SET
#define CREATE_GET_SET(PREFIX, FIELD_NAME,FIELD_DATATYPE) \
  protected:\
    FIELD_DATATYPE PREFIX ## FIELD_NAME;\
  public:\
  inline FIELD_DATATYPE get ## _ ## FIELD_NAME(void) const\
  { \
    return(PREFIX ## FIELD_NAME); \
  } \
  inline void set ## _ ## FIELD_NAME(FIELD_DATATYPE p) \
  { \
    PREFIX ## FIELD_NAME = p; \
  }
#endif

внутри класса/структуры вы должны определить переменную:

CREATE_GET_SET(_, id, unsigned int);

Это определит вашу переменную и создаст общий код getter/setter для кода. Это просто делает для более чистого, последовательного генерации кода для get/set. Конечно, вы можете написать все, но много кода типа шаблона. ПРИМЕЧАНИЕ: это всего лишь один из макросов пары. Я не публиковал их все. Вы бы не сказали "char *" таким образом (где вы хотите, чтобы набор был strncpy или strcpy данных). Это была просто демонстрация того, что вы могли бы сделать с макросом и несколькими простыми типами.

Ответ 8

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

Ответ 9

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

Аналогичный случай - когда у вас есть код сборки для одной конкретной системы, но код C для других систем.

Ответ 10

Я думаю, что самое большое преимущество при использовании в качестве "установочного файла"

#define USE_FAST_AGORHITM
//#define USE_SLOW_ONE

И глобальные "константы"

#define MAX_NUMBER_OF_USERS 100000

Существуют программы, написанные в основном в макросах (Check for пример реализации AES Брайана Гладмена http://www.gladman.me.uk/cryptography_technology/index.php)

Ответ 11

В макросах код функции вставляется в поток кода вызывающего абонента. Это может, в зависимости от многих других, повысить производительность, поскольку оптимизатор может процедурно интегрировать вызываемый код - оптимизировать вызываемый код в вызывающем Но будьте осторожны, потому что в макросах аргументы не проверяются. Хорошая ссылка на встроенные функции (если вы также используете С++), и немного макросов. http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.5