May 12th, 2011

moose, transparent

польза неопределенности (англ.)

(эта запись будет интересна только программистам и сочувствующим)

Интересная запись в блоге проекта LLVM о том, почему неопределенное поведение в стандарте C - полезная штука. Я знал эти аргументы теоретически (что неопределенное поведение позволяет компилятору более агрессивно оптимизировать код), но о подробных примерах никогда не задумывался:

Например:
  • если i - целая переменная с знаком, то компилятор может считать "i+1 > i" (или более сложное выражение, которое сводится к этому) автоматически истинным, несмотря на то, что для i==INT_MAX переполнение может сделать i+1 отрицательным. Если бы стандарт обязывал имплементацию C строго соблюдать "очевидную" логику переполнения, как это делает, например, Джава, то компилятор не смог бы почти никогда сделать эту оптимизацию.

    Подчеркну: дело тут не только в том, что стандарты C не обязывают имплементацию использовать 'дополнительный код' (two's complement) для представления отрицательных целых чисел. Даже если все имплементации используют это представление - что де-факто верно в современном мире - и даже если бы по другим причинам это представление было бы обязательным согласно стандарту, все равно может быть полезным оставить результат INT_MAX+1 неопределенным, а не "очевидным" - в точности по причине, объясненной в предыдущем абзаце. Эту довольно тонкую идею я недостаточно хорошо понимал, кажется.

  • Последствия обращения к NULL-указателю не определены. В Джаве такое обращение гарантированно кидает исключение, и в этом несомненно есть много своих преимуществ; но как следствие этого, компилятор не может менять порядок вычисления других выражений относительно обращения к указателю. Например, если на Джаве написано что-то вроде: "c = a.b; c+= 5;", то компилятор не может сгенерировать код, который сначала поместит в c 5, а потом добавит содержимое a.b. Потому что если a==NULL, то исключение должно произойти до того, как в c что-то помещено. А C/C++ таких гарантий не дает, поведение неопределенное, поэтому компилятор может изменить порядок вычисления. В данном дурацком примере это ничему не поможет, но бывают случаи, когда это может сильно ускорить вычисление.

Ну и еще в записи по ссылке есть несколько примеров такого рода.

Update: исходную запись почему-то удалили; я пока заменил своей копией.