В случае сомнений используйте функции (или встроенные функции).
Однако ответы здесь в основном объясняют проблемы с макросами, вместо того, чтобы иметь какое-то простое представление о том, что макросы - это зло, потому что возможны глупые аварии.
Вы можете знать о подводных камнях и учиться их избегать. Тогда используйте макросы только тогда, когда есть веская причина.
В определенных исключительных случаях есть преимущества использования макросов, в том числе:
- Общие функции, как указано ниже, могут иметь макрос, который может использоваться для различных типов входных аргументов.
- Переменное число аргументов может отображаться на разные функции вместо использования C
va_args
.
Например: https://stackoverflow.com/a/24837037/432509.
- Они могут опционально включать локальную информацию, такую как строки отладки:
(__FILE__
, __LINE__
, __func__
). проверять условия до / после, assert
при сбое или даже статические утверждения, чтобы код не компилировался при неправильном использовании (в основном полезно для отладочных сборок).
- Проверка входных аргументов. Вы можете выполнять тесты на входных аргументах, такие как проверка их типа, sizeof, проверка наличия
struct
членов перед приведением
(может быть полезно для полиморфных типов) .
Или проверьте, что массив соответствует некоторому условию длины.
см .: https://stackoverflow.com/a/29926435/432509
- Хотя отмечается, что функции выполняют проверку типов, C также будет приводить значения (например, ints / float). В редких случаях это может быть проблематично. Можно написать макросы, которые более требовательны, чем функция к их входным аргументам. см .: https://stackoverflow.com/a/25988779/432509
- Их использование в качестве оберток для функций, в некоторых случаях вы можете избежать повторения, например ...
func(FOO, "FOO");
, вы можете определить макрос, который расширяет строку для вас func_wrapper(FOO);
- Когда вы хотите манипулировать переменными в локальной области вызова, передача указателя на указатель работает нормально, но в некоторых случаях проще использовать макрос по-прежнему.
(присваивание нескольким переменным, для операций на пиксель, например, вы можете предпочесть макрос над функцией ... хотя он все еще сильно зависит от контекста, так как inline
функции могут быть опцией) .
По общему признанию, некоторые из них зависят от расширений компилятора, которые не являются стандартными C. Это означает, что вы можете получить менее переносимый код или использовать их ifdef
, так что они используются только тогда, когда компилятор поддерживает .
Предотвращение создания нескольких аргументов
Отмечая это, поскольку это одна из наиболее распространенных причин ошибок в макросах (например, с передачей x++
, где макрос может увеличиваться несколько раз) .
возможно написать макрос, который позволяет избежать побочных эффектов при множественном создании аргументов.
Если вы хотите иметь макрос square
, который работает с различными типами и имеет поддержку C11, вы можете сделать это ...
inline float _square_fl(float a) { return a * a; }
inline double _square_dbl(float a) { return a * a; }
inline int _square_i(int a) { return a * a; }
inline unsigned int _square_ui(unsigned int a) { return a * a; }
inline short _square_s(short a) { return a * a; }
inline unsigned short _square_us(unsigned short a) { return a * a; }
/* ... long, char ... etc */
#define square(a) \
_Generic((a), \
float: _square_fl(a), \
double: _square_dbl(a), \
int: _square_i(a), \
unsigned int: _square_ui(a), \
short: _square_s(a), \
unsigned short: _square_us(a))
Это расширение компилятора, поддерживаемое GCC, Clang, EKOPath и Intel C ++ (но не MSVC) ;
#define square(a_) __extension__ ({ \
typeof(a_) a = (a_); \
(a * a); })
Итак, недостаток макросов в том, что вы должны знать, как использовать их для начала, и что они не поддерживаются так широко.
Одним из преимуществ является то, что в этом случае вы можете использовать одну и ту же функцию square
для множества различных типов.