C: Возвращаемое значение через вопрос стека / регистра - PullRequest
15 голосов
/ 20 марта 2011

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

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

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

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

Спасибо.

Ответы [ 2 ]

18 голосов
/ 20 марта 2011

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

Например, относительно cdecl:

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

[...]

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

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

Уточнение:

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

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

4 голосов
/ 20 марта 2011

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

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

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

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

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