В соответствии со стандартами C и C++, если выполнение программы приводит к переполнению знаковой целой переменной, или к любому из сотен других «неопределённых действий» (undefined behaviour, UB), то результат выполнения программы может быть любым: она может запостить на Твиттер непристойности, может отформатировать вам диск…
Увы, в действительности «пасхальные яйца», которые бы заставляли программу в случае UB делать что-то из ряда вон выходящее, не встречались со времён GCC 1.17 — та запускала nethack, когда встречала в коде программы неизвестные #pragma
. Обычно же результат UB намного скучнее: компилятор просто оптимизирует код для тех случаев, когда UB не происходит, не придавая ни малейшего значения тому, что этот код будет делать в случае UB — ведь стандарт разрешает сделать в этом случае что угодно!
В качестве иллюстрации того, как изобилие UB в стандарте позволяет компилятору выполнять неочевидные оптимизации, Реймонд Чен приводит такой пример кода:
int table[4];
bool exists_in_table(int v)
{
for (int i = 0; i <= 4; i++) {
if (table[i] == v) return true;
}
return false;
}
В условии цикла мы ошиблись на единицу, поставив <=
вместо <
. В итоге exists_in_table()
либо должна вернуть true
на одной из первых четырёх итераций, либо она прочтёт table[4]
, что является UB, и в этом случае exists_in_table()
может сделать всё что угодно — в том числе, вернуть true
! В полном соответствии со стандартом, компилятор может соптимизировать код exists_in_table()
до
int table[4];
bool exists_in_table(int v)
{
return true;
}
Такие оптимизации иногда застают программистов врасплох. Читать полностью »