Неуточнённое поведение (Uyrmkcu~uuky hkfy;yuny)
Неуточнённое поведение (англ. unspecified behavior) и поведение, определяемое реализацией (англ. implementation-defined behavior) — поведение компьютерной программы, которое может различаться на разных платформах и компиляторах, поскольку спецификация языка программирования предлагает несколько допустимых вариантов реализации некой языковой конструкции. В отличие от неопределённого поведения, программа с неуточнённым поведением с точки зрения соответствия спецификации языка не считается ошибочной; при неуточняемом поведении, спецификация обычно ограничивает возможные варианты поведения, хотя и не сводит их в единое допустимое.
Разница между тем и другим такая: поведение, определяемое реализацией, задокументированное и последовательное на данном процессоре, программном окружении, версии системы и т. д. Неуточнённое поведение может меняться от случая к случаю, но система обязательно сделает что-то разумное — а не уйдёт в аварийный режим.
Программист должен избегать:
- Неопределённого поведения — всегда. Пример: доступ по NULL-указателю недопустим.
- Неуточнённого поведения — там, где оно критично для результата программы. Пример: если две функции вызываются в неуточнённом порядке и в них общий отладочный код, это будет видно в отладочном журнале, но для результата может быть и не критично.
- Но: если реализация уточняет неопределённое или неуточнённое поведение, программист может на неё закладываться. Примеры: хоть в Си переполнение знакового типа — это неопределённое поведение, на большинстве современных архитектур 32767+1=−32768. Если доступ по NULL-указателю обращается к вектору прерывания 0, или намеренно вызывает общую аварию программы, то это можно делать.
- Поведения, определяемого реализацией — если в числе поддерживаемых платформ есть такие, что ведут себя по-разному. Пример: большинство 8- и 16-битных платформ (в основном микроконтроллеры и старые компьютеры) говорят, что целый тип int — это два байта, но если поддерживаем только сравнительно мощные машины, можно считать, что int — четыре байта.
Терминология
[править | править код]Согласно стандарту языка C99,
- 3.4.1. поведение, определяемое реализацией (англ. implementation-defined behavior) — неуточняемое поведение, где каждая реализация документирует выбор поведения;
- 3.4.3. неуточняемое поведение (англ. unspecified behavior) — использование неуточняемого значения или иное поведение, где данный Международный стандарт предоставляет два или более варианта и не налагает никаких других требований на выбор в каждом конкретном случае.
Оригинальный текст (англ.)3.4.1 implementation-defined behaviorunspecified behavior where each implementation documents how the choice is made
[…]
3.4.3 unspecified behavior
use of an unspecified value, or other behavior where this International Standard provides two or more possibilities and imposes no further requirements on which is chosen in any instance— ISO/IEC 9899:201x [1]
Согласно стандарту языка C++,
- 1.3.5. поведение, определяемое реализацией (англ. implementation-defined behavior) — поведение правильно построенной программной конструкции с правильными данными которое зависит от реализации и которое должно быть документировано каждой реализацией;
- 1.3.13. неуточняемое поведение (англ. unspecified behavior) — поведение правильно построенной программной конструкции с правильными данными которое зависит от реализации. Реализация не обязана документировать выбор поведения. [Примечание: как правило, диапазон допустимых поведений указан в данном Международном стандарте.]
Оригинальный текст (англ.)1.3.5 implementation-defined behaviorbehavior, for a well-formed program construct and correct data, that depends on the implementation and that each implementation shall document.
[…]
1.3.13 unspecifed behavior
behavior, for a well-formed program construct and correct data, that depends on the implementation. The implementation is not required to document which behavior occurs. [Note: usually, the range of possible behaviors is delineated by this International Standard. ]
— ISO/IEC 14882:2003(E)
Примеры
[править | править код]В Си и C++ (в отличие от языка Java) порядок вычисления параметров функции является неуточняемым; следовательно, в программе, указанной ниже, порядок, в котором будут напечатаны строки «F» и «G», зависит от компилятора.
#include <iostream>
int f() {
std::cout << "F" << std::endl;
return 3;
}
int g() {
std::cout << "G" << std::endl;
return 4;
}
int h(int i, int j) {
return i + j;
}
int main() {
return h(f(), g());
}
Классическим примером поведения, определяемого реализацией (неуточняемого поведения, которое обязано быть документировано реализациями), является размер типов данных; например long в различных компиляторах и операционных системах может быть размером в 32 или 64 бит. Программа, которая предполагает, что в один long всегда поместится указатель, будет некорректно работать на некоторых платформах (например, в Windows x64)[2].
Вот две реализации быстрого обратного квадратного корня: реализация Кармака — Абраша (Quake III) и реализация на Си++20 из английской Википедии:
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
constexpr float Q_rsqrt(float number) noexcept
{
static_assert(std::numeric_limits<float>::is_iec559);
float const y = std::bit_cast<float>(
0x5f3759df - (std::bit_cast<std::uint32_t>(number) >> 1));
return y * (1.5f - (number * 0.5f * y * y));
}
Первая сделана для Windows и 32-битного Linux, вторая более универсальна: даёт ошибку компиляции, если на машине нестандартные дробные типы; не требует, чтобы long был 32-битным.
См. также
[править | править код]Примечания
[править | править код]- ↑ ISO/IEC 9899:201x Committee Draft — August 11, 2008 (англ.). Дата обращения: 1 декабря 2009. Архивировано 11 апреля 2012 года.
- ↑ size of long integer type on different architecture and OS (англ.). Intel Software Network. Дата обращения: 1 декабря 2009. Архивировано 11 апреля 2012 года.
Ссылки
[править | править код]- RSDN FAQ: Какая между ними разница (unspecified и undefined)? Архивная копия от 27 марта 2008 на Wayback Machine
- Мобильность на уровне исходных текстов Архивная копия от 18 сентября 2011 на Wayback Machine