Как говорится в названии; какая разница на практике между ключевым словом inline и директивой препроцессора #define?
Какая разница на практике между inline и #define?
Ответ 1
#define
является инструментом препроцессора и имеет макро семантику. Рассмотрим это, если max(a,b)
- макрос, определенный как
#define max(a,b) ((a)>(b)?(a):(b))
:
Пример 1:
val = max(100, GetBloodSample(BS_LDL))
будет проливать лишнюю невинную кровь, потому что функция фактически будет вызвана дважды. Это может означать значительную разницу в производительности для реальных приложений.
Пример 2:
val = max(3, schroedingerCat.GetNumPaws())
Это демонстрирует серьезную разницу в программной логике, потому что это может неожиданно вернуть число, которое меньше 3 - то, чего пользователь не ожидал бы.
Пример 3:
val = max(x, y++)
может увеличивать y
более одного раза.
С встроенными функциями ничего из этого не произойдет.
Основная причина заключается в том, что концепция макроса нацелена на прозрачность реализации (замену текстового кода) и встроенные цели для правильных языковых концепций, делая семантику вызова более прозрачной для пользователя.
Ответ 2
Функции (независимо от того, inline
или нет) и макросы выполняют разные цели. Их различие не следует рассматривать как идеологическое, как некоторые, похоже, воспринимают его, и что еще более важно, они могут хорошо работать вместе.
Макросы - это замена текста, которая выполняется во время компиляции, и они могут делать такие вещи, как
#define P99_ISSIGNED(T) ((T)-1 < (T)0)
который дает вам выражение времени компиляции, независимо от того, подписан или нет интегральный тип. То есть они идеально используются, когда тип выражения неизвестен (в определении), и вы хотите что-то сделать с этим. С другой стороны, ловушка с макросами состоит в том, что их аргументы могут оцениваться несколько раз, что плохо из-за побочных эффектов.
Настроены функции (inline
), что делает их более строгими или, выражаясь отрицательно, менее гибкими. Рассмотрим функции
inline uintmax_t absU(uintmax_t a) { return a; }
inline uintmax_t absS(uintmax_t a) {
return (-a < a) ? -a : a;
}
Первый реализует тривиальную функцию abs
для неподписанного интегрального типа. Второй реализует его для подписанного типа. (да, в аргументе используется unsigned, это для цели.)
Мы можем использовать их с любым интегральным типом. Но возвращаемый тип всегда будет иметь наибольшую ширину, и есть определенная трудность в понимании того, как выбирать между ними.
Теперь со следующим макросом
#define ABS(T, A) ((T)(P99_ISSIGNED(T) ? absS : absU)(A))
мы внедрили
- семейство функций
- который работает для любого интегрального типа
- который оценивает свой аргумент только один раз
- для которого любые недавние и достойные компилятор создаст оптимальный код
(Ну, я признаю, что делать это с помощью abs
немного искусственно, но я надеюсь, что вы получите картину.)
Ответ 3
Макросы (созданные с помощью #define
) всегда заменяются как написанные и могут иметь проблемы с двойной оценкой.
inline
, с другой стороны, является чисто консультативным - компилятор может игнорировать его. В стандарте C99 функция inline
также может иметь внешнюю связь, создавая определение функции, которое может быть связано с.
Ответ 4
Ну, многострочный #define
сложнее писать и редактировать, чем встроенную функцию. Вы можете определить встроенную функцию, как и любую нормальную функцию, и можете определять переменные без проблем. Представьте, что вы хотите вызвать блок кода несколько раз в рамках другой функции, и для этого блока кода нужны свои собственные переменные: это проще сделать с встроенными функциями (да, вы можете сделать это с помощью #defines и do { ... } while (0);
, но это то, что вы должны думать).
Кроме того, с включенной отладкой вы обычно получаете "смоделированный" стек-фрейм для встроенных функций, которые иногда могут облегчать отладку (по крайней мере, то, что вы получаете при компиляции/ссылке с gcc -g
и отладке с помощью GDB, IIRC). Вы можете разместить точки останова внутри своей встроенной функции.
Кроме того, результат должен быть почти идентичным, AFAIK.
Ответ 5
Различие здесь подробно описано здесь: http://www.geekinterview.com/question_details/23831 приветствия
Ответ 6
Функциональные макросы дают вам абсолютно нулевую проверку работоспособности в том месте, где они определены. Когда вы прикручиваете макрос, он отлично работает в одном месте и будет разбит где-то в другом месте, и вы не будете знать, почему, пока вы не потеряете несколько часов работы/времени сна. Функциональные макросы не работают с данными, они работают с исходным кодом. Иногда это хорошо, например, когда вы хотите использовать многоразовые отладочные операторы, в которых используются встроенные функции FILE и LINE, но большую часть времени это можно сделать с помощью встроенного функция, которая проверяется на синтаксис в точке определения, как и любая другая функция.