Возможный дубликат:
Лучший способ обнаружения переполнения целых чисел в C/С++
Нет, это не дубликат. Проблема та же, но вопрос другой.
Компилятор gcc может оптимизировать проверку переполнения (с -O2), например:
int a, b;
b = abs(a); // will overflow if a = 0x80000000
if (b < 0) printf("overflow"); // optimized away
Люди gcc утверждают, что это не ошибка. Overflow - это поведение undefined, в соответствии со стандартом C, которое позволяет компилятору что-либо делать. Видимо, что-то подразумевает, что переполнение никогда не происходит. К сожалению, это позволяет компилятору оптимизировать проверку переполнения.
Безопасный способ проверки переполнения описан в недавней документе CERT. В этой статье рекомендуется сделать что-то подобное перед добавлением двух целых чисел:
if ( ((si1^si2) | (((si1^(~(si1^si2) & INT_MIN)) + si2)^si2)) >= 0) {
/* handle error condition */
} else {
sum = si1 + si2;
}
По-видимому, вам нужно сделать что-то вроде этого перед каждыми +, -, *,/и другими операциями в серии вычислений, когда вы хотите убедиться, что результат действителен. Например, если вы хотите убедиться, что индекс массива не выходит за рамки. Это так громоздко, что практически никто этого не делает. По крайней мере, я никогда не видел программу C/С++, которая делает это систематически.
Теперь это фундаментальная проблема:
-
Проверка индекса массива перед доступом к массиву полезна, но не надежна.
-
Проверка каждой операции в серии вычислений с помощью метода CERT является надежной, но не полезной.
-
Заключение: нет полезного и надежного способа проверки переполнения в C/С++!
Я отказываюсь верить, что это было предназначено, когда был написан стандарт.
Я знаю, что есть определенные опции командной строки, которые могут решить проблему, но это не меняет того факта, что у нас есть фундаментальная проблема со стандартом или текущей интерпретацией.
Теперь мой вопрос: Являются ли люди gcc интерпретацией "undefined поведения" слишком далеко, когда он позволяет им оптимизировать проверку переполнения или нарушен стандарт C/С++?
Добавлено примечание: Извините, вы, возможно, неправильно поняли мой вопрос. Я не спрашиваю, как обойти проблему - на это уже ответил в другом месте. Я задаю более фундаментальный вопрос о стандарте C. Если нет полезного и надежного способа проверки переполнения, то сам язык сомнительный. Например, если я создаю класс безопасного массива с проверкой границ, тогда я должен быть в безопасности, но я не уверен, что проверка границ может быть оптимизирована.
Если стандарт позволяет это выполнить, то либо стандартная необходимость пересмотра, либо интерпретация стандарта нуждается в пересмотре.
Добавлено примечание 2: Люди здесь, похоже, не хотят обсуждать сомнительную концепцию поведения undefined. Тот факт, что в стандарте C99 перечислены 191 различные виды поведения undefined (ссылка), является показателем неаккуратного стандарта.
Многие программисты с готовностью принимают утверждение о том, что "undefined поведение" дает лицензии делать что-либо, включая форматирование вашего жесткого диска. Я думаю, что проблема заключается в том, что стандарт переводит целочисленное переполнение в ту же опасную категорию, что и запись границ внешнего массива.
Почему эти два типа поведения "w91 > " отличаются? Потому что:
-
Многие программы полагаются на целочисленное переполнение, являющееся доброкачественным, но мало программ полагаются на запись границ внешнего массива, когда вы не знаете, что там есть.
-
Написание внешних границ массива на самом деле может сделать что-то плохое, как форматирование вашего жесткого диска (по крайней мере, в незащищенной ОС, такой как DOS), и большинство программистов знают, что это опасно.
-
Когда вы помещаете целочисленное переполнение в опасную категорию "все идет", он позволяет компилятору что-либо делать, включая ложь о том, что он делает (в случае, когда проверка переполнения оптимизирована)
-
Ошибка, такая как запись границ внешнего массива, может быть найдена с помощью отладчика, но ошибка оптимизации проверки переполнения невозможна, поскольку при отладке обычно отключается оптимизация.
-
Компилятор gcc явно воздерживается от политики "ничего идет" в случае переполнения целых чисел. Есть много случаев, когда он воздерживается от оптимизации, например. цикл, если он не может проверить, что переполнение невозможно. По какой-то причине люди gcc признали, что у нас будет слишком много ошибок, если они будут следовать политике "все идет", но они имеют другое отношение к проблеме оптимизации проверки переполнения.
Возможно, это не подходящее место для обсуждения таких философских вопросов. По крайней мере, большинство ответов здесь не соответствует действительности. Есть ли лучшее место, чтобы обсудить это?