Какая разница на практике между inline и #define? - PullRequest
9 голосов
/ 24 августа 2010

Как гласит заголовок;В чем разница на практике между ключевым словом inline и директивой препроцессора #define?

Ответы [ 6 ]

40 голосов
/ 24 августа 2010

#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 голосов
/ 24 августа 2010

Функции (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 немного искусственно, но я надеюсь, что вы получите картину.)

1 голос
/ 24 августа 2010

Ну, многострочный #define сложнее писать и редактировать, чем встроенная функция.Вы можете определить встроенную функцию, как и любую обычную функцию, и можете без проблем определять переменные.Представьте, что вы хотите вызывать блок кода несколько раз внутри другой функции, и этому блоку кода нужны свои собственные переменные: это проще сделать с помощью встроенных функций (да, вы можете сделать это с помощью #defines и do { ... } while (0);, но это то, что вам нужнонадо подумать).

Кроме того, с включенной отладкой вы обычно получаете «симулированный» стек стека для встроенных функций, который иногда может облегчить отладку (по крайней мере, это то, что вы получаете, когда вы компилируете / связываете сgcc -g и отладка с GDB, IIRC).Вы можете размещать точки останова внутри встроенной функции.

Кроме того, результат должен быть практически одинаковым, AFAIK.

1 голос
/ 24 августа 2010

Макросы (созданные с помощью #define) всегда заменяются в том виде, в котором они написаны, и могут иметь проблемы с двойной оценкой.

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

0 голосов
/ 24 августа 2010

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

0 голосов
/ 24 августа 2010

Разница здесь довольно подробно описана: http://www.geekinterview.com/question_details/23831 ура

...