В этот документ является следующим примером фрагмента кода, который может вызвать деление на ноль:
if (arg2 == 0)
ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
/* No overflow is possible */
PG_RETURN_INT32((int32) arg1 / arg2);
ereport Вот макрос, который расширяется до вызова функции bool -returning errstart, которая может возвращаться или не возвращаться и, условно (используя ?:) по ее возвращаемому значению, вызывает другая функция. В этом случае я считаю, что ereport с уровнем ERROR безоговорочно вызывает a longjmp() где-то еще.
Следовательно, наивная интерпретация вышеприведенного кода заключается в том, что если arg2 отличное от нуля, произойдет деление, и результат будет возвращен, а если arg2 равен нулю, будет сообщено сообщение об ошибке и деление не случится. Однако связанная бумага утверждает, что компилятор C может законно поднять деление до нулевой проверки, а затем сделать вывод о том, что проверка нуля никогда не срабатывает. Их единственное рассуждение, которое мне кажется неправильным, заключается в том, что
Программист[T] не смог сообщить компилятору, что вызов ereport (ERROR,:) не возвращается. Это означает, что деление всегда будет выполняться.
У Джона Реджера более простой пример:
void bar (void);
int a;
void foo3 (unsigned y, unsigned z)
{
bar();
a = y%z;
}
Согласно этому сообщению в блоге, clang поднимает модульную операцию над вызовом bar, и он показывает некоторый код сборки, чтобы доказать это.
Мое понимание C, поскольку оно относится к этим фрагментам, заключалось в том, что
-
Функции, которые не возвращаются или не возвращаются, хорошо сформированы в стандартном C, а декларации таких не требуют особых атрибутов, колоколов или свистов.
-
Семантика вызова функции, которая не возвращает или не может возвращаться, четко определена, в частности, 6.5.2.2 "Вызов функций" на C99.
-
Так как вызов
ereportявляется полным выражением, в;есть точка последовательности. Аналогично, поскольку вызовbarв коде Джона Реджера является полным выражением, в;есть точка последовательности. -
Следовательно, существует точка последовательности между вызовом
ereportилиbarи деление или по модулю. -
Компиляторы C не могут вводить поведение undefined в программы, которые не вызывают поведение undefined самостоятельно.
Эти пять пунктов кажутся достаточными, чтобы сделать вывод о том, что вышеупомянутый тест деления на нуль правильно написан и что подъем по модулю выше вызова bar неверен. Два компилятора и множество экспертов не согласны. Что не так с моими рассуждениями?