Я не уверен на 100% в этом, но я попробую.
Сначала давайте рассмотрим несколько вещей, используемых здесь.
Оператор запятой отбрасывает первые результаты выражения n-1 и возвращает n-й результат.Он часто используется как точка последовательности , поскольку гарантируется, что выражения будут оцениваться по порядку.
Использование здесь __extension__
, что является GNUМакрос LibC, используется для маскировки любых предупреждений о специфичных для GNU расширениях в заголовках в средах компиляции, которые задают педантичные предупреждения, через -ansi
или -pedantic
и т. Д. Обычно при таких компиляторах использование специфичного для компилятора расширения приводит кпредупреждение (или ошибка, если вы работаете в -Werror
, что довольно распространено), но, поскольку в тех случаях, когда используются библиотеки и компиляторы GNU, libc позволяет себе использовать некоторые расширения, где это можно безопасно сделать.
Теперь, поскольку в реальной логике утверждения может использоваться расширение GNU (как указано при использовании __extension__
), любые реальные предупреждения, которые могло бы выдвинуть само выражение, учитывая его семантику (то естьвыражение, переданное в assert(expr)
) будет замаскировано, так как это выражение будет семантически расположено в блоке __extension__
и, таким образом, маскируется.
Следовательно, должен быть способ, позволяющий компилятору показывать эти предупреждения, но без оценки фактического выражения (поскольку выражение может иметь побочные эффекты, а двойная оценка может вызватьнежелательное поведение).
Вы можете сделать это с помощью оператора sizeof
, который принимает выражение, смотрит на его тип и находит количество используемых им символов - без фактической оценки выражения.
Например, если у нас есть функция int blow_up_the_world()
, то выражение sizeof(blow_up_the_world())
найдет размер результата выражения (в данном случае int
) без фактической оценки выражения.Использование sizeof()
в этом случае означало, что мир фактически не был бы взорван.
Однако, если expr
, переданный assert(expr)
, содержал код, который в противном случае вызвал бы предупреждение компилятора (например, используярасширение в режимах -pedantic
или -ansi
), компилятор все равно будет отображать эти предупреждения, даже если код находится внутри sizeof()
- предупреждений, которые в противном случае были бы замаскированы внутри блока __extension__
.
Далее мы видим, что вместо передачи expr
непосредственно в sizeof
они вместо этого используют троичный.Это связано с тем, что тип тернар - это тот тип, который имеют оба результирующих выражения - в данном случае это int
или что-то эквивалентное.Это происходит потому, что передача определенных вещей в sizeof
приведет к значению runtime , а именно в случае массивов переменной длины , которые могут иметь нежелательные эффекты,или может выдать ошибку, например, когда передает sizeof
имя функции .
Наконец, они хотели всего этого, но до фактической оценки и хотели сохранить assert()
каквыражение, поэтому вместо использования do{}while()
блока или чего-то подобного, что в конечном итоге приведет к тому, что assert()
будет оператором, вместо этого они использовали оператор запятой, чтобы отбросить результат первого sizeof()
трюка.