Подтвердить что ты не робот

C: Возвращаемое значение через стек/регистр

Я новичок в C, и есть одна вещь, которую я не могу понять. Когда функция возвращает то, что не больше регистра - мой компилятор помещает его в EAX. Когда я возвращаю большую структуру (не указатель, а сама структура) - он возвращается через стек.

Мой вопрос: как компилятор знает, как вызывать функцию, экспортированную другим объектом? Существуют вызывающие соглашения (например, stdcall), но речь идет о передаче аргументов, а не чтении возвращаемого значения, правильно?

Должно быть какое-то правило типа "Если значение возврата объявлено больше EAX, чем взять его из [bp -...]".

И еще одно: правильно ли было бы сказать, что объекты, которые я хочу вернуть, больше, чем регистр, должны храниться в куче и возвращаться указателем, чтобы предотвратить все операции со стеком?

Спасибо.

4b9b3361

Ответ 1

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

Например, в отношении cdecl:

Используется соглашение о вызове cdeclмногими системами C для x86 архитектура. В cdecl функция параметры вставляются в стек в право налево. Возврат функции значения возвращаются в EAX регистр (за исключением плавающей запятой значения, возвращаемые в x87 регистр ST0).

[...]

В интерпретация cdecl, в частности как вернуть значения. В результате, x86, скомпилированные для разных операционных систем и/или разные компиляторы могут быть несовместимы, даже если они оба используют cdecl и не вызывать в окружающую среду. Некоторые компиляторы возвращают простые данные структуры длиной 2 регистров или меньше в EAX: EDX и более крупные структуры и объекты класса требующих специального лечения обработчик исключений (например, определенный конструктор, деструктор или назначение) возвращаются в память. к передать "в память", вызывающий абонент выделяет памяти и передает указатель на него как скрытый первый параметр; вызываемый заполняет память и возвращает указатель, всплывающий скрытый указатель при возврате.

Манипуляции стеками будут намного быстрее, чем манипуляции с кучей, необходимыми, если вы выделите память в куче, так что стек всегда быстрее. Единственная причина (в C), которую вы, возможно, захотите вернуть указатель на что-то в куче, состоит в том, что она не будет помещаться в стек.

Разъяснение:

В последнем предложении выше "единственная причина, по которой вы захотите..." не следует интерпретировать как "обычно нет причин возвращать указатель". Скорее, я имею в виду "если вы можете делать то, что вам нужно, не возвращая указатель, единственная причина, по которой нужно использовать указатель, - это...".

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

Другими словами, возвращает значение, когда вы можете; используйте указатели, когда вы должны.

Ответ 2

И еще одно: правильно ли было бы сказать, что объекты, которые я хочу вернуть, больше, чем регистр, должны храниться в куче и возвращаться указателем, чтобы предотвратить все операции со стеком?

Ну, может быть. Честно говоря, выбор "return by pointer" или "return by value" - это тот, который, вероятно, должен иметь лучшие причины, чем "Я хочу, чтобы возвращение было быстрее". Например, быстрее было бы возвращать через указатель, чем через стек для больших объектов, но это не учитывает большее количество времени, затрачиваемого на выделение объекта в куче по сравнению со стеком.

Что еще более важно, return-by-pointer позволяет вам иметь непрозрачные указатели, объекты с разным размером и определенные степени полиморфного поведения, которые невозможны в объектах стека. Если вы хотите или нуждаетесь в таких видах поведения, вы все равно должны использовать return-by-pointer. Если вы этого не сделаете, вы можете использовать return-by-value, или вы можете передать указатель на объект, выделенный пользователем (как бы он им не понравился), в качестве параметра и изменить этот параметр в вашей функции (это иногда называют "out" или что-то подобное).

Выберите метод возврата, основанный на том, что вам нужно, и что делает ваш код, а не то, что вы думаете быстрее. Если вы обнаружите, что вам абсолютно нужна скорость (после профилирования и определения того, что возврат является узким местом), тогда беспокоитесь об этой микро-оптимизации.