Что произойдет, если вы не вернете значение на С++?

Вчера я обнаружил, что пишу код следующим образом:

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 в функция возврата значения.

Я бы сказал, что в этом случае нет причин для компилятора не давать ошибку. Почему так много компиляторов дают только предупреждение или вообще не диагностируют?

Ответ 6

Вы не получили предупреждения, потому что у вас не было -Wall -Werror. (Как указано в других ответах)

Однако я думаю, что вы, вероятно, получили нулевую структуру как результат, потому что объект стека был по умолчанию сконфигурирован в функции вызывающего абонента, возможно с явными нулевыми аргументами, или из-за нулей в стеке?