Когда использовать подобные функции макросы в C

Смотрите на эта статья о MSDN об "индуктивных пользовательских интерфейсах". Это описывает платформу (и обеспечивает код для загрузки) на основе UserControls, которые дают Вам "навигацию" в форме. Идеально подходящий для разработки мастеров.

11
задан a3f 31 March 2015 в 01:09
поделиться

6 ответов

Одним из важных преимуществ реализации на основе макросов является то, что она не привязана к какому-либо конкретному типу параметра. Подобный функции макрос в C действует во многих отношениях как функция шаблона в C ++ (шаблоны в C ++ родились как «более цивилизованные» макросы, BTW). В данном конкретном случае аргумент макроса не имеет конкретного типа. Это может быть абсолютно все, что можно преобразовать в тип unsigned long . Например, если пользователю это нравится (и если он готов принять последствия, определяемые реализацией), он может передавать типы указателей в этот макрос.

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

#define ABS(x) ((x) >= 0 ? (x) : -(x))

работает со всеми арифметическими типами, в то время как реализация на основе функций должна предоставлять довольно много из них (я имею в виду стандартные abs , лаборатории , labs и fabs ). (И да, я осведомлен о традиционно упоминаемых опасностях таких макросов.)

Макросы не идеальны, но популярное изречение о том, что «функционально-подобные макросы больше не нужны из-за встроенных функций» - это просто чепуха. Чтобы полностью заменить функционально-подобные макросы, C потребуются шаблоны функций (как в C ++) или, по крайней мере, перегрузка функций (как снова в C ++). Без этого функционально-подобные макросы были и останутся чрезвычайно полезным основным инструментом в C.

16
ответ дан 3 December 2019 в 01:04
поделиться

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

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

Некоторые проблемы, с которыми я столкнулся при поддержке кода, написанного злоумышленниками, заключались в том, что макросы могут выглядеть как функции, но не отображаться в таблице символов, так что может быть очень утомительно пытаться отследить их до их происхождения в разрастающихся наборах кодов (где это определено ?!). Написание макросов ВСЕМИ ЗАГЛАВНЫМИ буквами, очевидно, будет полезно будущим читателям.

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

3
ответ дан 3 December 2019 в 01:04
поделиться

Ваш пример на самом деле вообще не функция,

#define HASH(fp) (((unsigned long)fp)%NHASH)
//  this is a cast ^^^^^^^^^^^^^^^
//  this is your value 'fp'       ^^
//  this is a MOD operation          ^^^^^^

Я бы подумал, это просто способ написать более читаемый код с приведением типов и настройками, заключенными в один макрос ' HASH (fp) '


Теперь, если вы решите написать функцию для этого, это, вероятно, будет выглядеть так:

int hashThis(int fp)
{
  return ((fp)%NHASH);
}

Довольно излишне для функции как таковой,

  • вводит вызов точка
  • вводит настройку и восстановление стека вызовов
1
ответ дан 3 December 2019 в 01:04
поделиться

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

Преимущества выполнения макросов были устранены, когда C ++ представил встроенные функции. Многие старые API, такие как MFC и ATL, по-прежнему используют макрофункции для выполнения трюков препроцессора, но это просто оставляет код запутанным и трудным для чтения.

0
ответ дан 3 December 2019 в 01:04
поделиться

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

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

3
ответ дан 3 December 2019 в 01:04
поделиться

Подобные макросы позволяют избежать накладных расходов на вызов функции

Это может показаться не таким уж большим. Но в вашем примере макрос превращается в 1-2 инструкции на машинном языке, в зависимости от вашего процессора:

  • Получить значение fp из памяти и поместить его в регистр
  • Взять значение в регистре, выполнить модуля (%) вычисление фиксированного значения и оставьте это в том же регистре

, тогда как эквивалент функции будет намного больше инструкций машинного языка, обычно что-то вроде

  • Вставьте значение fp в стек
  • Вызов функции, которая также помещает следующий (возвратный) адрес в стек
  • Возможно создание кадра стека внутри функции, в зависимости от архитектуры ЦП и соглашения ABI
  • Получить значение fp из стека и поместить его в регистре
  • Возьмите значение в регистре, выполнить вычисление модуля (%) по фиксированному значению и оставить его в том же регистре
  • Возможно, взять значение из регистра и вернуть его в стек, в зависимости от ЦП и ABI
  • Если кадр стека был построено, раскрутите его
  • Удалите адрес возврата из стека и возобновите выполнение инструкций там

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

Лично я предпочитаю использовать встроенный C ++ как более читаемый и менее подвержены ошибкам, но встроенные строки также являются скорее подсказкой для компилятора, которую он не должен принимать. Макросы препроцессора - это кувалда, с которой компилятор не может спорить.

и оставьте это в том же регистре
  • Может быть, взять значение из регистра и поместить его обратно в стек, в зависимости от ЦП и ABI
  • Если кадр стека был создан, развернуть его
  • Удалить адрес возврата стек и возобновить выполнение инструкций там
  • Намного больше кода, а? Если вы делаете что-то вроде рендеринга каждого из десятков тысяч пикселей в окне в графическом интерфейсе, все будет работать намного быстрее, если вы используете макрос.

    Лично я предпочитаю использовать встроенный C ++ как более читаемый и менее подвержены ошибкам, но встроенные строки также являются скорее подсказкой для компилятора, которую он не должен принимать. Макросы препроцессора - это кувалда, с которой компилятор не может спорить.

    и оставьте это в том же регистре
  • Может быть, взять значение из регистра и поместить его обратно в стек, в зависимости от CPU и ABI
  • Если фрейм стека был создан, раскрутите его
  • Удалите адрес возврата стек и возобновить выполнение инструкций там
  • Намного больше кода, а? Если вы делаете что-то вроде рендеринга каждого из десятков тысяч пикселей в окне в графическом интерфейсе, все будет работать намного быстрее, если вы используете макрос.

    Лично я предпочитаю использовать встроенный C ++ как более читаемый и менее подвержены ошибкам, но встроенные строки также являются скорее подсказкой для компилятора, которую он не должен принимать. Макросы препроцессора - это кувалда, с которой компилятор не может спорить.

    развернуть
  • Вытащить адрес возврата из стека и возобновить выполнение инструкций там
  • Намного больше кода, а? Если вы делаете что-то вроде рендеринга каждого из десятков тысяч пикселей в окне в графическом интерфейсе, все будет работать намного быстрее, если вы используете макрос.

    Лично я предпочитаю использовать встроенный C ++ как более читаемый и менее подвержены ошибкам, но встроенные строки также являются скорее подсказкой для компилятора, которую он не должен принимать. Макросы препроцессора - это кувалда, с которой компилятор не может спорить.

    развернуть
  • Вытащить адрес возврата из стека и возобновить выполнение инструкций там
  • Намного больше кода, а? Если вы делаете что-то вроде рендеринга каждого из десятков тысяч пикселей в окне в графическом интерфейсе, все будет работать намного быстрее, если вы используете макрос.

    Лично я предпочитаю использовать встроенный C ++ как более читаемый и менее подвержены ошибкам, но встроенные строки также являются скорее подсказкой для компилятора, которую он не должен принимать. Макросы препроцессора - это кувалда, с которой компилятор не может спорить.

    Я предпочитаю использовать C ++ inline как более читаемый и менее подверженный ошибкам, но inline также действительно является скорее подсказкой для компилятора, которую он не должен принимать. Макросы препроцессора - это кувалда, с которой компилятор не может спорить.

    Я предпочитаю использовать C ++ inline как более читаемый и менее подверженный ошибкам, но inline также действительно является скорее подсказкой для компилятора, которую он не должен принимать. Макросы препроцессора - это кувалда, с которой компилятор не может спорить.

    24
    ответ дан 3 December 2019 в 01:04
    поделиться
    Другие вопросы по тегам:

    Похожие вопросы: