Деление на ноль в C
Когда я компилирую программу:
#include <stdio.h>
int main(void)
{
int x, y = 0;
x = 1 / y;
printf("x = %d\n", x);
return 0;
}
Это дает "Исключение с плавающей точкой (ядро сброшено)"
Тем не менее, когда я компилирую:
#include <stdio.h>
int main(void)
{
double x, y = 0;
x = 1 / y;
printf("x = %f\n", x);
return 0;
}
Печатает "х = инф"
Почему он возвращает x как бесконечное значение, если вы используете double, но возвращает ошибку, если вы используете int?
3 ответа
Стандарт C явно заявляет, что деление на ноль имеет неопределенное поведение для целочисленных операндов или операндов с плавающей точкой.
C11 6.5.5 пункт 5:
Результат
/
оператор является частным от деления первого операнда на второй; результат%
оператор является остатком. В обеих операциях, если значение второго операнда равно нулю, поведение не определено.
Неопределенное поведение означает, что стандарт ничего не говорит о том, что происходит. Это может привести к значимому или бессмысленному результату, может привести к краху или, как говорится в стандартной шутке, заставить демонов вылететь из носа. (Конечно, последнего не произойдет, но это не нарушит стандарт С, если это произойдет.)
Типы с плавающей точкой, в отличие от целочисленных типов, часто имеют специальные значения, которые не представляют числа. Стандарт IEEE с плавающей точкой определяет, что деление на ноль может привести к результату Infinity, что и делает ваша реализация. Не существует значения "Бесконечность" для целых чисел. (Обратите внимание, что реализации C могут или не могут соответствовать стандарту IEEE с плавающей запятой.)
В этом вопросе обсуждается семантика деления на ноль в IEEE с плавающей точкой.
Стандарт C определяет, что поведение деления на ноль для операндов арифметических типов не определено (см., Например, этот онлайн-проект стандарта C):
6.5.5 Мультипликативные операторы
2 Каждый из операндов должен иметь арифметический тип. Операнды оператора% должны иметь целочисленный тип.
5 Результатом оператора / является частное от деления первого операнда на второй; результат оператора% - остаток. В обеих операциях, если значение второго операнда равно нулю, поведение не определено.
И это правило явно включает значения с плавающей запятой, поскольку термин арифметические типы означает целые значения и значения с плавающей запятой:
6.2.5 Типы
18 Целочисленные и плавающие типы вместе называются арифметическими типами.
Следовательно, оба ваших примера на самом деле являются неопределенным поведением, и каждый компилятор может сам указать, как обращаться с вашими утверждениями. И, таким образом, он соответствует стандарту, если компилятор рассматривает деление на ноль как исключение, тогда как деление на ноль с плавающей точкой дает особое значение inf
, Примечание: стандарт не определяет, что это должно быть так.
Переменная с плавающей точкой может фактически хранить значение, представляющее бесконечность. (Это значение INFINITY
определено в math.h.) Но нет целочисленного представления бесконечности, поэтому единственное, что нужно сделать, это потерпеть неудачу.