Как `({...})` возвращает значение?
Недавно я нашел этот макрос GCC:
#define max(a,b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a > _b ? _a : _b; })
Я не понял, прежде чем увидел этот код, что блок кода {...}
может каким-то образом вернуть значение в C.
1) Не могли бы вы дать мне подсказку, как это работает?
Хотя я обычно мог добиться такого же результата, злоупотребляя оператором запятой:
#define max(a,b) \
(typeof (a) _a = (a), \
typeof (b) _b = (b), \
(_a > _b ? _a : _b))
или если бы это было только для побочного эффекта, я использовал бы do { ... } while(0)
2) Каков предпочтительный способ сделать это?
Ответы
Ответ 1
Конструкция ({ ... })
является расширением gcc.
Итак, это оператор typeof
.
Макрос A MAX
(обратите внимание на обычное использование всех шапок) достаточно легко написать:
#define MAX(a, b) ((a) > (b) ? (a) : (b))
Он оценивает один из своих аргументов более одного раза, поэтому вы не должны вызывать его, например, MAX(x++, y--)
. Использование all-caps служит для напоминания пользователю о том, что это макрос, а не функция, и быть осторожным в аргументах с побочными эффектами.
Или вы можете написать функцию (возможно, встроенную) для каждого типа.
Ответ 2
Это расширение GCC. Оператор запятой не работает:
// C89, doesn't work...
#define max(a,b) \
(typeof (a) _a = (a), \
typeof (b) _b = (b), \
(_a > _b ? _a : _b))
Оператор запятой работает только с выражениями, а typeof(a) _a = (a);
является объявлением, а не выражением. На самом деле невозможно написать эквивалентный макрос без GCC-расширений или C11, который имеет _Generic
. Обратите внимание, что typeof
также является расширением GCC, поэтому вы не получаете какой-либо переносимости, исключив ({...})
, если вы также не устраните typeof
.
Вот версия C11, обратите внимание на то, насколько подробно он сравнивается (и он обрабатывает только два типа!). C11 еще не поддерживается, удачи, пытаясь найти компилятор, чтобы проверить это:
// C11
static inline int maxi(int x, int y) { return x > y ? x : y; }
static inline long maxl(long x, long y) { return x > y ? x : y; }
#define max(x, y) _Generic((x), \
long: maxl(x,y), \
int:_Generic((y), \
int: maxi(x,y), \
long: maxl(x,y)))
В переносном C99 вы можете написать макрос или встроенную функцию, которая достигает такого же эффекта, за исключением того, что она будет работать только для одного типа для макроса.
// C99
static inline int maxi(int x, int y) { return x > y ? x : y; }
В C89/C90 я не могу думать о каком-либо способе написать макрос таким образом, что он не будет дважды оценивать x
или y
.