Согласовано. ПРАВИЛЬНЫЙ СПОСОБ ПРОВЕРИТЬ ПЕРЕХОД В C / C ++ АРИФМАТИЧЕСКИЙ. Есть причина, по которой в стандарте указано завернуть!

это не дубликатКак обнаружить целочисленное переполнение?, Вопрос тот же, но вопрос другой.

Компилятор gcc может оптимизировать проверку переполнения (с -O2), например:

int a, b;
b = abs(a);                     // will overflow if a = 0x80000000
if (b < 0) printf("overflow");  // optimized away

Люди из gcc утверждают, что это не ошибка. Переполнение - неопределенное поведение, согласно стандарту C, которое позволяет компилятору делатьчто-нибудь, По всей видимости,что-нибудь включает допущение, что переполнение никогда не происходит. К сожалению, это позволяет компилятору оптимизировать проверку переполнения.

Безопасный способ проверки на переполнение описан в недавнемБумага CERT, В этой статье рекомендуется сделать что-то подобное, прежде чем добавлять два целых числа:

if ( ((si1^si2) | (((si1^(~(si1^si2) & INT_MIN)) + si2)^si2)) >= 0) { 
  /* handle error condition */
} else {
  sum = si1 + si2;
}

По-видимому, вы должны делать что-то подобное перед каждыми +, -, *, / и другими операциями в серии вычислений, когда хотите убедиться, что результат верен. Например, если вы хотите убедиться, что индекс массива не выходит за пределы. Это так громоздко, что практически никто этим не занимается. По крайней мере, я никогда не видел программы на C / C ++, которая делает это систематически.

Теперь это фундаментальная проблема:

Проверка индекса массива перед обращением к массиву полезна, но не надежна.

Проверка каждой операции в серии расчетов методом CERT надежна, но бесполезна.

Вывод: в C / C ++ нет полезного и надежного способа проверки переполнения!

Я отказываюсь верить, что это было задумано, когда был написан стандарт.

Я знаю, что есть определенные параметры командной строки, которые могут решить проблему, но это не меняет того факта, что у нас есть фундаментальная проблема со стандартом или его текущая интерпретация.

Теперь мой вопрос: люди gcc слишком сильно интерпретируют «неопределенное поведение», когда оно позволяет оптимизировать проверку переполнения, или нарушен стандарт C / C ++?

Добавлено примечание: Извините, вы, возможно, неправильно поняли мой вопрос. Я не спрашиваю, как обойти проблему - на это уже ответилив другом месте, Я задаю более фундаментальный вопрос о стандарте C. Если нет полезного и надежного способа проверки на переполнение, то сам язык сомнительный. Например, если я создаю безопасный класс массива с проверкой границ, я должен быть в безопасности, но я не уверен, что проверка границ может быть оптимизирована.

Если стандарт допускает это, то либо стандарт требует пересмотра, либо интерпретация стандарта требует пересмотра.

Добавлено примечание 2: Люди здесь не хотят обсуждать сомнительную концепцию «неопределенного поведения». Тот факт, что стандарт C99 перечисляет 191 различных видов неопределенного поведения (ссылка) является признаком небрежного стандарта.

Многие программисты с готовностью принимают утверждение, что «неопределенное поведение» дает лицензию на любые действия, включая форматирование жесткого диска. Я думаю, что проблема в том, что стандарт помещает целочисленное переполнение в ту же опасную категорию, что и запись за пределы массива.

Почему эти два вида «неопределенного поведения» различны? Потому что:

Многие программы полагаются на то, что целочисленное переполнение является доброкачественным, но немногие программы полагаются на запись вне границ массива, когда вы не знаете, что там есть.

Запись вне границ массива на самом делеМожно Сделайте что-нибудь настолько же плохое, как форматирование жесткого диска (по крайней мере, в незащищенной ОС, такой как DOS), и большинство программистов знают, что это опасно.

Когда вы помещаете целочисленное переполнение в опасную категорию «все идет», это позволяет компилятору делать что угодно, включая ложь о том, что он делает (в случае, когда проверка на переполнение оптимизирована)

Ошибка, такая как запись за пределы массива, может быть найдена с помощью отладчика, но ошибка оптимизации без проверки переполнения не может быть выполнена, поскольку при отладке оптимизация обычно отключена.

Компилятор gcc явно воздерживается от политики «все идет» в случае целочисленного переполнения. Есть много случаев, когда он воздерживается от оптимизации, например, цикл, если он не может проверить, что переполнение невозможно. По какой-то причине люди из gcc признали, что у нас было бы слишком много ошибок, если бы они следовали здесь политике «все идет», но у них другое отношение к проблеме оптимизации проверки переполнения.

Может быть, это не то место для обсуждения таких философских вопросов. По крайней мере, большинство ответов здесь не по теме. Есть ли лучшее место, чтобы обсудить это?

Ответы на вопрос(4)

Ваш ответ на вопрос