Что происходит, когда функция, возвращающая объект, заканчивается без оператора return
В С++, что происходит, когда функция, которая должна возвращать объект, заканчивается без оператора return? Что возвращается?
например.
std::string func() {}
Ответы
Ответ 1
Что возвращается?
Мы не знаем. Согласно стандарту, поведение undefined.
$6.6.3/2 Оператор return
[Stmt.return]:
(акцент мой)
Отбрасывание конца конструктора, деструктора или функции с возвращаемым типом cv void
эквивалентно return
без операнда. В противном случае при выходе из функции, отличной от main
(basic.start.main), вы получите undefined поведение.
Фактически большинство компиляторов дадут предупреждение для него, например Clang:
предупреждение: управление достигает конца не-void функции [-Wreturn-type]
Ответ 2
В С++, что происходит, когда функция, которая должна возвращать объект, заканчивается без оператора return?
Он вызывает поведение undefined. Никто не может сказать, что именно произойдет.
Ответ 3
Мне было любопытно, поэтому я сделал несколько тестов на Visual С++ 2015.
int f()
{
if (false)
return 42;
// oops
}
int main()
{
int i = f();
}
Мне пришлось добавить if
, чтобы получить предупреждение вместо жесткой ошибки:
> cl /nologo /FAs /c a.cpp
a.cpp(6) : warning C4715: 'f': not all control paths return a value
Сгенерированный код сборки довольно прост, и я удалил ненужные части. Здесь мясо f()
:
f:
xor eax, eax
je label
mov eax, 42
label:
ret
Строка xor
в основном eax=0
. Поскольку if (false)
является постоянным условием, сгенерированный код даже не пытается выполнить сравнение, а затем будет безоговорочно переходить на label
, который просто возвращается из функции. Вы можете видеть, что "возвращаемое значение" (42
) действительно будет храниться в eax
, но эта строка никогда не будет выполнена. Поэтому eax == 0
.
Здесь main()
делает:
call f
mov _i$[ebp], eax
ret
Он вызывает f()
и слепо копирует eax
в место в стеке (где i
есть). Поэтому i == 0
.
Попробуйте что-то более сложное с объектом и конструктором:
struct S { int i=42; };
S f()
{
if (false)
return {};
// oops
}
int main()
{
S s = f();
}
Что main()
делает в основном резерв sizeof(S)
байтов в стеке, поместите адрес первого байта в eax
, а затем вызовите f()
:
lea eax, _s$[ebp]
push eax
call f
Опять же, f()
ничего не сделает, поскольку он безоговорочно перейдет к концу функции:
f:
xor eax, eax
je label
; some stuff
; call S::S(), which would set i to 42
; but none of that will happen
label:
ret
Итак, что случилось с байтами sizeof(S)
в главном? Они никогда не менялись. Они содержат то, что уже было в памяти в этом конкретном месте. Они содержат мусор.
Это с неоптимизированной сборкой в данной версии данного компилятора. Измените компилятор, измените его. Включить оптимизатор резко изменить поведение.
Не делай этого.