В чем разница на практике между 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, но в большинстве случаев это можно сделать с помощью встроенной функции, которая проверяется на синтаксис в точке определения, как и любая другая функция.