Неуточнённое поведениеНеуточнённое поведение (англ. unspecified behavior) и поведение, определяемое реализацией (англ. implementation-defined behavior) — поведение компьютерной программы, которое может различаться на разных платформах и компиляторах, поскольку спецификация языка программирования предлагает несколько допустимых вариантов реализации некой языковой конструкции. В отличие от неопределённого поведения, программа с неуточнённым поведением с точки зрения соответствия спецификации языка не считается ошибочной; при неуточняемом поведении, спецификация обычно ограничивает возможные варианты поведения, хотя и не сводит их в единое допустимое. Разница между тем и другим такая: поведение, определяемое реализацией, задокументированное и последовательное на данном процессоре, программном окружении, версии системы и т. д. Неуточнённое поведение может меняться от случая к случаю, но система обязательно сделает что-то разумное — а не уйдёт в аварийный режим. Программист должен избегать:
ТерминологияСогласно стандарту языка C99,
Согласно стандарту языка C++,
ПримерыВ Си и 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-битным. См. такжеПримечания
Ссылки
|
Portal di Ensiklopedia Dunia