Это совершенно непредсказуемое поведение программы, или оно укладывается в какие-то рамки дозволенного ?
С точки зрения языка это совершенно непредсказуемое поведение программы, без каких-либо рамок. При этом нужно учитывать что:
1. Неопределенное поведение может проявляться уже на стадии компиляции. А именно: допустимым проявлением неопределенного поведения является отказ компилятора компилировать "неопределенную" программу.
2. Конкретные реализации имеют полное право "доопределять" неопределенное поведение: делать его определенным или ограничивать диапазон его допустимых проявлений "рамками дозволенного" (т.е. фактически переводить его в разряд
неспецифицированного поведения).
---
Одним из эквивалентных определений неопределенного поведения являются следующее: "Компилятор при трансляции программы имеет право быть уверенным, что обстоятельства, приводящие к неопределенному поведению, никогда не возникнут".
Например, при трансляции
int x;
...
if (x < x + 1)
компилятор имеет право полагать, что
x + 1 заведомо не вызывает арифметического переполнения, а значит условие
if является заведомо истинным.
Поэтому надо понимать, что неопределенное поведение не является неким локализованным проявлением какой-то конкретной конструкции, которую вы вписали в свой код со словами "пофиг, я знаю как это будет работать". Основным источником настоящей (!) непредсказуемости в неопределенном поведении является то, что компилятор в рамках вышеназванной "уверенности" запросто может начать далеко не локально оптимизировать/трансформировать ваш код и в результате изменить его поведение до неузнаваемости. Вы не можете предсказать, что компилятор сотворит с неопределенным кодом.
Ссылаясь на вышеприведенный пример с
if, зачастую можно услышать чайницкий лепет в стиле "у мне на платформе знаковое переполнение не вызывает неопределенного поведения, у меня будет заворот значения в отрицательные, я знаю, я видел в ассемблере" и т.п. чушь. Компилятору языка глубоко плевать, на ваши знания и на что вы там "видели в ассемблере". Компилятор языка вправе полагать, что условие
x < x + 1 заведомо истинно и
будет это полагать, если этот компилятор захочет, чтобы его воспринимали всерьез.
Другой известный пример: компилятор GCC "назло" пользователю возвращает из функций нулевые указатели при явных попытках возвращения указателей/ссылок на локальные переменные, заткнув глотки отдельной ораве "знатоков" и их болтовне о том, что мол, "я знаю, что возвращать указатели на локальные переменные плохо, но я смотрел в ассемблере, там в стеке лежат вполне правильные значения".
Еще один замечательный и очень показательный пример - пример для
clang, в котором делается попытка вызова функции через нулевой указатель
#include <stdlib.h>
static int (*Do)(void);
static int EraseAll(void) {
return system("echo \"I'm formatting your hard drive!\"");
}
void NeverCalled(void) {
Do = EraseAll;
}
int main() {
return Do();
}
http://coliru.stacked-crooked.com/a/24e543923f868e20---
Поэтому когда в рамках рассмотрения программы с неопределенным поведением некие индивидуумы начинают блеять какой-то детский лепет из разряда "это будет работать правильно, я знаю, потому что так говорит спецификация while" и т.п. феерическую чушь - сразу можно выносить вердикт, что данное дарование программировать на С или С++ не будет.