Неопределенное поведение по существу определяется тем, что оно не приводит к легко диагностируемой ошибке. Считайте в https://en.cppreference.com/w/cpp/language/ub. Продвинутый компилятор может обнаружить его, но это не обязательно.
Это настоящая проблема UB - он действительно может делать именно то, что вы хотите. Пока этого не произойдет, что приведет к ошибкам, которые не срабатывают сначала, что делает их потенциально трудными для исправления, когда они, наконец, появляются.
Что компилятор может сделать с вашим фрагментом кода, это:
- Память для
tmp
зарезервирована в стеке . - Создается ссылка. Давайте для простоты действовать так, как если бы это был обычный указатель. По сути, используется адрес этой памяти.
- Возвращается ссылка.
- Когда функция остается, все в стековой памяти, принадлежащей функции, освобождается. Это означает, что эти адреса памяти больше не зарезервированы, и другой процесс может зарезервировать их вместо этого.
- Однако: нет необходимости тратить какое-либо время на какие-либо действия с содержимым памяти в этой позиции. Он по-прежнему содержит десять в целочисленном формате.
- Ваша консольная печать обращается к памяти по адресу. Хотя он не зарезервирован, он все же содержит значение. Поскольку ваша переменная
t
объявлена как целое число, она считывает эту часть памяти как единицу, а десять все еще там, все работает нормально.
Поскольку эта часть памяти не зарезервировано после возврата, что-то еще может зарезервировать его и записать в него до того, как произойдет консольная печать. Если это так, то на выходе может быть что-то вроде -36234646346.
Итак, во всем этом я много предполагал о том, как работает компилятор. Это не дано. Ваш вопрос
«Если бы вас спросили, что будет делать этот фрагмент кода, вы бы сказали, что он печатает нормально или с ошибкой компилятора?» однозначного ответа нет. Можно сказать, что он может делать то, что вы хотите, но это не дано.
Распространенная шутка заключается в том, что, поскольку компилятору разрешено делать все, что угодно , если код имеет неопределенное поведение, он также приводит к тому, что рой демонов выходит из вашего носа .
Это, конечно, преувеличено, но дело в том, что неопределенное поведение может делать многое, чего вы действительно не ожидаете .
Например: посмотрите на это: http://www.cpp.sh/32x7f. Там доступ к двумерному массиву осуществляется за пределами вывода консоли. Что происходит, когда вы его запускаете, так это то, что он продолжает итерацию, распечатывая переменную итератора с более чем сотней, в то время как for l oop должен был остановиться после трех. Я понятия не имею, что там происходит, доступ за пределы не должен иметь возможности связываться с l oop, но, видимо, это так. (Обратите внимание, что глобальная переменная в нем имеет плохой стиль, но не связана с поведением.)
Важно: научитесь распознавать неопределенное поведение и никогда не полагаться на его работу определенным образом.