В этот документ является следующим примером фрагмента кода, который может вызвать деление на ноль:
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
неверен. Два компилятора и множество экспертов не согласны. Что не так с моими рассуждениями?