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