Следующий фрагмент взят из mac sdk signal.h:
#define sigemptyset(set) (*(set) = 0, 0)
Просто интересно, что делает , 0)?
@PSkocik: прототип функции присутствует в заголовочном файле и реализован в библиотеке, макрос допускает встроенное расширение, жертвуя проверкой типов, чтобы сократить несколько циклов.
@chqrlie Проверка типов с помощью *(sigset_t*){set} была бы тривиальной для более новых C, у которых есть составные литералы. Можно сделать и с древними функциями C: *(0?(sigset_t*)0:(set)). Для codegen макрос, конечно, лучше, чем вызов функции для такой тривиальной вещи, но ненужные отклонения от стандарта меня немного раздражают (что иронично, потому что я даже не очень люблю стандарты). В любом случае, хороший ответ. +1
@PSkocik: интересные способы проверки типов в макросах без множественной оценки и расширений выражений операторов!
Макрос расширяется до выражения с запятой: левый операнд, оцениваемый первым, устанавливает объект, на который указывает set, на 0, затем оценивается правый операнд, и его значение является значением всего выражения, следовательно: 0.
Другими словами, макрос ведет себя как функция, которая всегда завершается успешно, на успех указывает возвращаемое значение 0.
За исключением проверки типа, макрос эквивалентен:
#include <signal.h>
int sigemptyset(sigset_t *set) {
*set = 0;
return 0;
}
Обратите внимание, что заголовочный файл <signal.h> также содержит прототип этой функции:
int sigemptyset(sigset_t *);
Тот же трюк используется для других функций в заголовочном файле, для которых , 0) необходим, чтобы выражение оценивалось как 0 с типом int:
#define sigaddset(set, signo) (*(set) |= __sigbits(signo), 0)
#define sigdelset(set, signo) (*(set) &= ~__sigbits(signo), 0)
#define sigemptyset(set) (*(set) = 0, 0)
#define sigfillset(set) (*(set) = ~(sigset_t)0, 0)
Я бы добавил, что оператор запятая здесь не нужен, потому что левое выражение равно 0, но тот же шаблон, вероятно, используется для других макросов, заменяющих связанные функции, такие как sigfillset или sigaddset, где выражение присваивания не равно 0.
Тот же шаблон действительно используется для других макросов в <signal.h>, и использование , 0) приводит к тому, что выражение оценивается как 0 с типом int без приведения. sigset определяется как __uint32_t, определение макроса как #define sigemptyset(set) (*(set) = 0) будет иметь тип unsigned, а определение его как #define sigemptyset(set) ((int)(*(set) = 0)) не лучше, чем использование оператора запятой.
В этом выражении
(*(set) = 0, 0)
Используется оператор запятая.
Результат выражения равен 0.
В качестве побочного эффекта объекту, на который указывает указатель set, присваивается значение 0.
Из стандарта C (оператор запятой 6.5.17)
2 Левый операнд оператора запятой оценивается как пустота выражение; существует точка последовательности между его оценкой и тем, что правого операнда. Затем оценивается правый операнд; результат имеет свой тип и значение.
Я не думаю, что это соответствующая реализация. POSIX говорит, что это должна быть функция с прототипом int sigemptyset(sigset_t *set);. При использовании макросов они также должны проверять тип аргумента и предоставлять реализацию функции.