Как `({...})` возвращает значение?

Недавно я нашел этот макрос 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.