[БЕЗ_ЗВУКА] В предыдущих видео мы с вами познакомились с положительными аспектами в неопределенном поведении. Теперь настала пора посмотреть, как неопределенное поведение может осложнить жизнь программистам. Допустим, мне нужно написать программу, которая по названию небесного тела скажет, является ли оно планетой. Хорошо. Я еще со школьных времен помню, что в Солнечной системе у нас девять планет. Поэтому я заведу вот такую константу девять — это количество планет. И дальше перечислю все их названия. Названия у нас — это строковые литералы, поэтому я их сохраню как массив string_view. Для использования string_view добавлю соответствующий заголовочный файл. Теперь у меня есть полный список всех планет, и я напишу функцию IsPlanet, которая принимает название некоторого небесного тела и должна вернуть true в том случае, если оно является планетой. Для этого я пробегусь по всем элементам в нашем массиве. kPlanetsCount, и посмотрю, если у нас у нас очередной элемент (planets[i] равен тому, что нам передали, тогда нам нужно вернуть true. В противном случае вернуть false, если мы прошли по всему циклу и не нашли соответствия ни одному из наших элементов. Хорошо. Теперь давайте заведем небольшую тестовую функцию, она будет принимать на вход некоторое название. И соответственно говорить, как это название классифицируется: как планета или нет. Она пишет нам name is. Дальше, в зависимости от того, планета это или нет. Если это планета, пусть это будет просто is. В противном случае она нам скажет NOT. a planet. Хорошо. И давайте теперь запустим эту нашу тестовую функцию с несколькими названиями. Скажем, у нас там будет Земля, Венера, Плутон и Луна. Замечательно. Давайте запустим такую программу. Программа у нас запустилась и даже сразу заработала. Замечательно. Она нам говорит, что Земля, Венера и Плутон — это планеты, а Луна — это не планета. И вроде бы все достаточно логично. В общем, я написал программу, сдал ее на поддержку. Поддерживать ее, оказалось, вам. Вы ее взяли и встроили в какой-нибудь backend своего сервиса. Этот сервис обрабатывает запросы и отвечает, является там что-то планетой или не является. А потом через некоторое время к вам приходит bug report, в котором говорится, что... Я заканчивал школу в 2004 году, а в 2006 году Международный астрономический союз переклассифицировал Плутон как карликовую планету. И оказывается, что их в Солнечной системе аж как минимум пять штук. То есть то, что наша программа считает Плутон планетой, это неправильно. Надо ее исправить. Вы идете и исправляете. Какие проблемы? Вот у вас программа, вот у вас список планет, вы берете и убираете из списка Плутон. Очень легко. Казалось бы, что может пойти не так? И запускаете программу, чтобы убедиться, что тесты проходят правильно. И вот тут происходит достаточно неожиданная ситуация. То есть ваша программа не только все еще продолжает считать Плутон планетой, но кроме того, она стала считать, что Луна теперь тоже планета. И вот это достаточно странно. И вы думаете: что за ерунда? Да быть такого не может. Не знаю, может быть, как-то поотлаживаться что ли. Но чтобы поотлаживаться, наверное, нужно брать оптимизации, так отлаживаться удобно. Вы убираете оптимизации и запускаете ту же самую программу. И внезапно оказывается, что теперь она начинает работать правильно. Она говорит, что ни Плутон, ни Луна планетой не являются. Все правильно. «Что за ерунда?» — думаете вы. «Может быть, какая-то проблема в компиляторе? Из-за компилятора такое происходит?» И выбираете другой компилятор. Давайте, был gcc, поставим clang. И clang тоже соберем с оптимизациями. И запускаете теперь. И видите, что в этом компиляторе все работает правильно. И возможно, считаете, что проблема все-таки в компиляторе. На самом деле, как вы догадались, проблема, конечно же, не в компиляторе. Проблема в том, что у нас в программе появилось неопределенное поведение. Давайте посмотрим на него более внимательно. Вот наша функция, и из нашего массива планет, в котором перечислены все названия, мы убрали одну планету. Его размер теперь равен восьми. А константу, в которой у нас хранилась его размерность, мы оставили, и она равна все еще девяти. Поэтому как рассуждает компилятор в данном случае? Он думает: вообще говоря, возможно ровно две ситуации. Во-первых, name может содержаться в списке планет. В этом случае нужно вернуть true. Вроде бы очевидно. Хорошо. Вторая ситуация: name не содержится в списке планет. Что происходит тогда? Тогда цикл доходит до конца, и мы обращаемся к элементу с индексом 9, а его в нашем массиве планет нет. И поэтому у нас возникает неопределенное поведение. Но неопределенное поведение, ведь мы же помним, что программист обещал, что неопределенного поведения в нашей программе не будет. И поэтому компилятор такой случай может не рассматривать. И поэтому он рассматривает только тот случай, который у него остается, что name содержится в списке планет, и тогда нужно будет вернуть true. Собственно, нашу догадку мы можем проверить в уже известном нам сервисе compiler explorer. Давайте засунем в него вот эту нашу функцию и посмотрим, что он нам выведет. И действительно, смотрите, он показывает, что функция IsPlanet состоит ровно из одной инструкции, мы ее уже встречали mov eax 1, которая возвращает в регистр, в котором хранится выходное значение, единичку. Единичка — это boolean, который равен true. Получается, что неопределенное поведение действительно не определено. На что мог бы понадеяться программист? Ну ладно, цикл, дойдет до последнего элемента, он посмотрит на этот элемент, этот элемент уже вылезает за границы массива. Наверное, он найдет там какой-то мусор, сравнит с этим мусором. Скорее всего, у него ничего не получится, и функция так или иначе вернет false. Да, такое может произойти. И да, скорее всего, ровно это и происходило, когда мы отключали оптимизацию или когда мы выбирали другой компилятор. Но совершенно необязательно, что произойдет именно это. Компилятор вправе сделать все, что угодно. Поэтому не нужно ожидать, что компилятор сделает что-то конкретное. Кроме того, мы убедились, что в случае неопределенного поведения программа может вести себя совершенно по-разному, для разных настроек, разных компиляторов и разных версий одного и того же компилятора.