Что произойдет, если вы не вернете значение на С++?
Вчера я обнаружил, что пишу код следующим образом:
SomeStruct getSomeStruct()
{
SomeStruct input;
cin >> input.x;
cin >> input.y;
}
Конечно, забываем фактически вернуть только что созданную структуру. Как ни странно, значения в структуре, которые были возвращены этой функцией, были инициализированы до нуля (при компиляции с использованием g++, который есть). Это просто совпадение или другой SomeStruct создается и инициализируется где-то неявно?
Ответы
Ответ 1
Был ли другой SomeStruct создан и инициализирован где-то неявно?
Подумайте, как возвращается структура. Если оба x
и y
равны 32 битам, они слишком велики, чтобы вписаться в регистр в 32-битной архитектуре, и то же самое относится к 64-битным значениям в 64-битной архитектуре (ответы @Denton Gentry как более простые значения возвращаются), поэтому его нужно где-то выделять. Было бы бесполезно использовать кучу для этого, поэтому он должен быть выделен в стеке. Но он не может быть в фрейме стека вашей функции getSomeStruct
, так как это недействительно больше после возвращения функции.
Вместо этого компилятор обращается к вызываемой функции, где поместить результат (который, вероятно, где-то в стек вызывающего), передав вызываемой функции скрытым указателем на выделенное ему пространство. Таким образом, место, где оно установлено в ноль, находится на вызывающем, а не на вашей функции getSomeStruct
.
Существуют также оптимизации, такие как "оптимизация возврата имени", где могут быть удалены дополнительные копии. Итак, если бы вы использовали отсутствующий return
, результат был бы создан непосредственно на пространстве, выделенном вызывающим, вместо создания временного и копирования.
Чтобы узнать больше о том, что происходит, вы должны посмотреть на функцию вызывающего абонента. Является ли это инициализацией (до нуля) "пустой" SomeStruct
, которую вы позже назначили возвращаемому значению вашей функции getSomeStruct
? Или он делает что-то еще?
Ответ 2
Отказ от конца функции, объявленной для возврата значения (без явного возврата значения), приводит к последствиям undefined. Для gcc вы должны начать с ключа командной строки -Wall
, который включает самые полезные предупреждения. Специфическое предупреждение gcc, которое управляет предупреждением, которое вы хотите, это -Wreturn-type
(которое включено в -Wall
, я просто упомянул об этом для полноты).
После включения предупреждений вы также должны использовать -Werror
для обработки предупреждений как ошибок и сделать остановку сборки в точке, где она обнаруживает ошибку.
Ответ 3
Вызывающие соглашения для большинства современных архитектур процессоров указывают конкретный регистр для передачи возвращаемого значения функции обратно вызывающему. Вызывающий вызывает вызов функции, а затем использует указанный регистр как возвращаемое значение. Если вы явно не возвращаете значение, вызывающий абонент, тем не менее, будет использовать любой мусор, находящийся в этом регистре.
Компилятор также будет использовать все регистры, доступные для внутренних вычислений внутри этой функции. Регистр, предназначенный для хранения возвращаемого значения, также будет использоваться для расчета в рамках функции. Таким образом, когда вы забыли указать возвращаемое значение, нет ничего необычного в том, чтобы найти правильное значение, которое чудесным образом было возвращено вызывающему: компилятор использовал этот регистр для хранения вашего объекта.
К сожалению, даже тривиальное изменение функции может привести к изменению распределения регистров, поэтому возвращаемое значение становится истинным мусором.
Ответ 4
Я нахожу это интересным. При использовании параметров по умолчанию следующие компиляторы имеют следующее поведение при компиляции функции GetSomeStruct()
:
-
Microsoft VC, все версии (поскольку VC6 в любом случае):
error C4716: 'getSomeStruct' : must return a value
-
Цифровой Марс:
Warning 18: implied return of getSomeStruct at closing '}' does not return value
-
Комо:
warning: missing return statement at end of non-void function "getSomeStruct"
-
gcc:
нет ошибки или предупреждения
Учитывая следующую пару предложений из стандарта (6.6.3. Пункт 2):
Оператор возврата без выражение может быть использовано только в функции, которые не возвращают значение, то есть функция с возвратом тип void, конструктор (12.1) или деструктор (12.4).... Стекание конец функции эквивалентен возврат без ценности; это результаты в режиме undefined в функция возврата значения.
Я бы сказал, что в этом случае нет причин для компилятора не давать ошибку. Почему так много компиляторов дают только предупреждение или вообще не диагностируют?
Ответ 5
Для меня компилятор не допустил этого: http://codepad.org/KkzVCesh
Ответ 6
Вы не получили предупреждения, потому что у вас не было -Wall -Werror
. (Как указано в других ответах)
Однако я думаю, что вы, вероятно, получили нулевую структуру как результат, потому что объект стека был по умолчанию сконфигурирован в функции вызывающего абонента, возможно с явными нулевыми аргументами, или из-за нулей в стеке?