Если у меня есть такая функция:
int addNumbers(int x, int y)
{
return x + y;
}
и если я использую его как таковой:
cout << addNumbers(4, 5) << endl;
Он вернется и распечатает 9
, Используя то же самое cout
строка выше, если я закомментирую или удаляю возврат в addNumbers
, он вернется и распечатает 1
, Если я сделаю это:
int addNumbers(int x, int y)
{
int answer = x + y;
//return x + y;
}
Он автоматически вернется и распечатает 9
без меня с помощью возврата. Точно так же я могу написать int answer = x
; и он вернется 4
, Я также могу написать это:
int addNumbers(int x, int y)
{
int answer = x;
answer = 1;
//return x + y;
}
и он все равно вернется 4.
Что именно возвращается и почему? Он только возвращает что-то отличное от 1, когда я использую переменные параметра, но не возвращает ответ переменной, как показано в последнем примере, потому что я изменил его на 1, и он все еще вернул значение x (4)
,
§6.6.3 [stmt.return] / p2:
Вытекающий из конца функции эквивалентен
return
без
значение; это приводит к неопределенному поведению в возвращающем значение
функция.
(main()
это особое исключение. Стекает с конца main()
эквивалентно return 0;
)
Допустимые УБ включают в себя:
А если серьезно, UB может проявляться во всех видах способов. Например, учитывая этот код:
#include <iostream>
bool foo = false;
int addNumbers(int x, int y)
{
int answer = x;
answer = 1;
//return x + y;
}
int main(){
if(!foo) {
addNumbers(10, 20);
std::cout << 1 << std::endl;
}
else {
std::cout << 2 << std::endl;
}
}
лязг ++ в -O2 печать 2
,
Зачем? Потому что это выводило, что addNumbers(10, 20);
имеет неопределенное поведение, что позволяет предположить, что первая ветвь никогда не берется и что foo
всегда true
даже если это явно не так.
Вы полагаетесь на «неопределенное поведение». Возвращаемое значение для простых типов обычно хранится в регистре, который также может использоваться при формировании результата вычисления. Но он также НЕ может быть использован, и вы получите произвольный «случайный» результат, и, будучи «неопределенным поведением», вы также можете получить любую другую возможную операцию, которую может выполнить ваш компьютер — например, сбой или выполнение некоторого кода, который вы не сделали хочу выполнить …
Вы наблюдаете неопределенное поведение. Нет веской причины «почему» программа делает это, потому что это не правильно сформированная программа. Он может сделать что угодно, в том числе удалить себя с диска при запуске. Включить предупреждения и ошибки компилятора (например, g++ -Wall -Wextra -Werror
) и вам будет автоматически запрещено писать такой код (как и должно быть).
Таким образом, это неопределенное поведение, разборка вашего двоичного файла может объяснить, почему возвращаются такие значения.
objdump -d example.bin
Поскольку возвращаемое значение связано с реестром rax, если компилятор использует rax для обработки функции, возвращаемое значение является значением, которое остается в rax.
В любом случае, вы не должны этого делать, потому что когда вы пишете такой код, оптимизации компилятора и использование реестров неизвестны.