Нет полезного и надежного способа обнаружения целочисленного переполнения в 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 осознали, что у нас было бы слишком много ошибок, если бы они следовали здесь политике «все идет», но у них другое отношение к проблеме оптимизации, отказавшись от проверки переполнения.

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

11
задан Cœur 13 July 2018 в 13:52
поделиться