Может ли макрос C содержать временные переменные?
У меня есть функция, которую мне нужно макровизировать. Функция содержит временные переменные, и я не могу вспомнить, существуют ли какие-либо правила использования временных переменных в макроподстановках.
long fooAlloc(struct foo *f, long size)
{
long i1, i2;
double *data[7];
/* do something */
return 42;
}
Форма MACRO:
#define ALLOC_FOO(f, size) \
{\
long i1, i2;\
double *data[7];\
\
/* do something */ \
}
Это нормально? (то есть никакого неприятного побочного эффекта - кроме обычных: не "безопасный тип" и т.д.). Кстати, я знаю, что "макросы злы" - я просто должен использовать его в этом случае - не так много выбора.
Ответы
Ответ 1
Есть только два условия, при которых он работает любым разумным способом.
-
У макроса нет оператора return. Вы можете использовать трюк do while
.
#define macro(x) do { int y = x; func(&y); } while (0)
-
Вы нацеливаете только GCC.
#define min(x,y) ({ int _x = (x), _y = (y); _x < _y ? _x : _y; })
Это поможет, если вы объясните, почему вы должны использовать макрос (есть ли в вашем офисе "макропомощи" или что-то в этом роде?). В противном случае мы не сможем помочь.
Ответ 2
Во-первых, я настоятельно рекомендую встроенные функции. Макросов очень мало, и они не могут, и они гораздо чаще будут делать то, что вы ожидаете.
Одна ложь макросов, которую я не видел в других ответах, - это затенение имен переменных.
Предположим, вы определили:
#define A(x) { int temp = x*2; printf("%d\n", temp); }
И кто-то использовал его так:
int temp = 3;
A(temp);
После предварительной обработки код:
int temp = 3;
{ int temp = temp*2; printf("%d\n", temp); }
Это не работает, потому что внутренняя температура затеняет внешний.
Общим решением является вызов переменной __temp
, предполагая, что никто не будет определять переменную с использованием этого имени (что является странным предположением, учитывая, что вы только что это сделали).
Ответ 3
C макросы - это (относительно простые) текстовые подстановки.
Итак, вопрос, который вы, возможно, спрашиваете: могу ли я создать блоки (также называемые составными операторами) в функции, подобной приведенной ниже в примере?
void foo(void)
{
int a = 42;
{
int b = 42;
{
int c = 42;
}
}
}
и ответ да.
Теперь, когда @DietrichEpp упомянул об этом в своем ответе, если макрос представляет собой составной оператор, как в вашем примере, хорошей практикой является приложение макросов с помощью do { ... } while (0)
, а не только { ... }
. Ссылка ниже объясняет, в какой ситуации do { ... } while (0)
в макросе пытается предотвратить:
http://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html
Также, когда вы пишете функционально-подобный макрос, всегда спрашивайте себя, есть ли у вас реальное преимущество, потому что чаще всего писать функцию лучше.
Ответ 4
Это в основном ОК, за исключением того, что макросы обычно заключены в do { ... } while(0)
(посмотрите на этот вопрос для объяснений):
#define ALLOC_FOO(f, size) \
do { \
long i1, i2;\
double *data[7];\
/* do something */ \
} while(0)
Кроме того, насколько ваша исходная функция fooAlloc
возвращает long
, вам нужно изменить свой макрос, чтобы сохранить результат как-то иначе. Или, если вы используете GCC, вы можете попробовать составной оператор:
#define ALLOC_FOO(f, size) \
({ \
long i1, i2;\
double *data[7];\
/* do something */ \
result; \
})
Наконец, вы должны заботиться о возможных побочных эффектах расширения аргумента макроса. Обычный шаблон определяет временную переменную для каждого аргумента внутри блока и вместо этого использует их:
#define ALLOC_FOO(f, size) \
({ \
typeof(f) __f = (f);\
typeof(size) __size = (size);\
long i1, i2;\
double *data[7];\
/* do something */ \
result; \
})
Ответ 5
Ответ Eldar показывает вам большинство ошибок макропрограммирования и некоторого полезного (но не стандартного) расширения gcc.
Если вы хотите придерживаться стандарта, может быть полезно сочетание функций макросов (для общей) и inline
(для локальных переменных).
inline
long fooAlloc(void *f, size_t size)
{
size_t i1, i2;
double *data[7];
/* do something */
return 42;
}
#define ALLOC_FOO(T) fooAlloc(malloc(sizeof(T)), sizeof(T))
В таком случае использование sizeof
оценивает выражение для типа во время компиляции, а не для его значения, поэтому это не будет дважды оценивать F
.
Кстати, "размеры" обычно должны быть напечатаны с помощью size_t
, а не с long
или аналогичным.
Изменить: Что касается вопроса Джонатана о функциях inline
, я написал что-то о модели inline
модели C99, здесь.
Ответ 6
Да, он должен работать, когда вы используете структуру блоков, а временные переменные объявляются во внутренней области этого блока.
Обратите внимание, что последний\после} является избыточным.
Ответ 7
Они могут. Они часто не должны.
Почему эта функция должна быть макросом? Не могли бы вы включить его?
Ответ 8
Если вы используете С++, используйте inline или используйте -o3 с gcc, он будет встроить все функции для вас.
Я до сих пор не понимаю, почему вам нужно макросоциализировать эту функцию.
Ответ 9
Не идеальное решение: (не работает с рекурсивными макросами, например несколькими петлями внутри друг друга)
#define JOIN_(X,Y) X##Y
#define JOIN(X,Y) JOIN_(X,Y)
#define TMP JOIN(tmp,__LINE__)
#define switch(x,y) int TMP = x; x=y;y=TMP
int main(){
int x = 5,y=6;
switch(x,y);
switch(x,y);
}
станет после запуска препроцессора:
int main(){
int x=5,y=6;
int tmp9 = x; x=y; y=tmp9;
int tmp10 = x; x=y; y=tmp10;
}