Что произойдет, если вы не вернете значение в C ++? - PullRequest
14 голосов
/ 16 ноября 2008

Вчера я обнаружил, что пишу такой код:

SomeStruct getSomeStruct()
{
    SomeStruct input;

    cin >> input.x;
    cin >> input.y;
}

Конечно, забыв вернуть фактически созданную мной структуру. Как ни странно, значения в структуре, которые были , возвращенные этой функцией, были инициализированы нулем (при компиляции с использованием g ++). Это просто совпадение или другой SomeStruct был создан и инициализирован где-то неявно?

Ответы [ 6 ]

20 голосов
/ 16 ноября 2008

Падение конца функции, объявленной для возврата значения (без явного возврата значения), приводит к неопределенным последствиям. Для gcc вы должны начать с ключа командной строки -Wall, который включает наиболее полезные предупреждения. Конкретное предупреждение gcc, которое управляет желаемым предупреждением, - -Wreturn-type (которое включено в -Wall, я просто упомяну это для полноты).

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

8 голосов
/ 16 ноября 2008

Соглашения о вызовах для большинства современных архитектур ЦП указывают конкретный регистр для передачи возвращаемого значения функции вызывающей стороне. Вызывающая сторона выполняет вызов функции, а затем использует указанный регистр в качестве возвращаемого значения. Если вы явно не вернете значение, вызывающая сторона, тем не менее, будет использовать любой мусор в этом регистре.

Компилятор также будет использовать все регистры, которые он имеет для внутренних вычислений внутри функции. Регистр, предназначенный для хранения возвращаемого значения, также будет использоваться для вычисления misc внутри функции. Таким образом, когда вы забыли указать возвращаемое значение, нередко обнаружилось, что правильное значение чудесным образом было возвращено вызывающей стороне: компилятор использовал этот регистр для хранения вашего объекта.

К сожалению, даже тривиальное изменение функции может привести к изменению распределения регистров, поэтому возвращаемое значение становится истинным мусором.

7 голосов
/ 16 ноября 2008

Был ли другой SomeStruct создан и инициализирован где-то неявно?

Подумайте, как возвращается структура. Если и x, и y являются 32-битными, это слишком большой размер, чтобы поместиться в регистр 32-битной архитектуры, и то же самое относится к 64-битным значениям в 64-битной архитектуре (ответ @Denton Gentry упоминает как более простые значения возвращаются), поэтому он должен быть где-то размещен. Для этого было бы расточительно использовать кучу, поэтому она должна быть размещена в стеке. Но он не может быть в фрейме стека вашей getSomeStruct функции, так как он больше не действителен после возврата из функции.

Компилятор вместо этого заставляет вызывающую функцию сообщать вызываемой функции, куда поместить результат (который, вероятно, находится где-то в стеке вызывающей стороны), передавая вызываемой функции скрытый указатель на выделенное ей пространство. Таким образом, место, где он устанавливается на ноль, находится на вызывающей стороне , а не на вашей getSomeStruct функции.

Существуют также оптимизации, такие как «оптимизация возврата именованных значений», в которых могут быть исключены дополнительные копии. Итак, если бы вы использовали отсутствующий return, результат был бы создан непосредственно на месте, выделенном вызывающей стороной, вместо создания временного и его копирования.

Чтобы узнать больше о происходящем, вам нужно взглянуть на функцию вызывающего абонента. Является ли инициализация (нулем) «пустого» * ​​1018 *, которому вы позже назначаете возвращаемое значение вашей функции getSomeStruct? Или это делает что-то еще?

3 голосов
/ 16 ноября 2008

Я нахожу это интересным. При использовании параметров по умолчанию следующие компиляторы ведут себя следующим образом при компиляции функции GetSomeStruct():

  • Microsoft VC, все версии (начиная с VC6 в любом случае):

    error C4716: 'getSomeStruct' : must return a value

  • Цифровой Марс:

    Warning 18: implied return of getSomeStruct at closing '}' does not return value

  • Comeau:

    warning: missing return statement at end of non-void function "getSomeStruct"

  • gcc:

    без ошибок или предупреждений

Учитывая следующую пару предложений из стандарта (6.6.3, абзац 2):

оператор возврата без Выражение может быть использовано только в функции, которые не возвращают значение, то есть функция с возвратом тип void, конструктор (12.1) или деструктор (12.4). ... вытекает конец функции эквивалентен возврат без значения; это результаты в неопределенном поведении в функция, возвращающая значение.

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

2 голосов
/ 16 ноября 2008

Для меня компилятор этого не допустил: http://codepad.org/KkzVCesh

1 голос
/ 16 ноября 2008

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

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

...