Fastcall c что это
Русские Блоги
Познакомьтесь с соглашениями о вызовах Visual Studio __cdecl, __stdcall и __fastcall
Те, кто имеет определенный опыт разработки на C ++, должны быть знакомы с «__cdecl, __stdcall, __fastcall»! Но ты действительно понимаешь? Да, я собрал здесь бесчисленные ямы, посадил бесчисленные каблуки и, наконец, у меня нет другого выбора, кроме как подвести итог (хотя у меня уже есть возможность решить большинство из этих проблем)!
Что называется конвенция
функциональнаяСоглашение о вызовахКак следует из названия, это ограничение и спецификация (спецификация) для вызовов функций, описывающая, как передаются параметры функций и кто очищает стек. Он определяет следующее: (1) порядок, в котором параметры функции помещаются в стек, (2) извлекает ли вызывающий или вызываемый объект параметры из стека, и (3) и метод генерации имени модификатора функции.
Мы знаем, что функция состоит из следующих частей: Тип возвращаемого значения Имя функции (список параметров), например:
【code1】
Выше перечислены хорошо известные компоненты, на самом деле, есть еще некоторые компоненты функции, то есть соглашение о вызовах. следующим образом:
【code2】
Соглашение о вызовах должно быть одинаковым в объявлении и определении
В VC ++ соглашение о вызовах является частью типа функции,Следовательно, соглашение о вызовах объявления и определения функции должно быть одинаковым. Вы не можете просто иметь соглашение о вызовах в объявлении, и определение не отличается от объявления или не отличается от него.следующим образом:
[code3] Неправильно используйте один:
error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__stdcall *)(int,int)’ to ‘int’
[Code4] Два неправильных использования:
error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__cdecl *)(int,int)’ to ‘int’
[Code5] Три неправильных использования:
error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__stdcall *)(int,int)’ to ‘int’
[Code6] Правильное использование:
Процесс вызова функции
Процесс вызова функции может быть описан следующим образом:
(1) Сначала поместите базовый адрес (ebp) стека вызывающего абонента (A) в стек, чтобы сохранить информацию предыдущей задачи.
(2) Затем присвойте значение указателя верхнего стека (esp) вызывающей стороны (A) для ebp в качестве нового базового адреса (то есть нижнего стека вызываемого B).
(3) Затем по этому базовому адресу (в нижней части стека вызываемого абонента B) соответствующее пространство (обычно с использованием подинструкции) используется в качестве пространства стека вызываемого абонента B.
(4) После возврата из функции B ebp текущего кадра стека восстанавливается до вершины стека вызывающей стороны A (esp), так что вершина стека восстанавливается до позиции, предшествующей вызову функции B, затем Затем вызывающая сторона A может извлечь предыдущее значение ebp с вершины восстановленного стека (это можно сделать, поскольку это значение было помещено в стек за один шаг до вызова функции). Таким образом, ebp и esp восстанавливают позицию перед вызовом функции B, то есть состояние до вызова функции восстановления стека B.
Этот процесс завершается в сборке AT & T двумя инструкциями, а именно:
Особенности __cdecl
(1). Чтобы убедиться, что параметры помещены в стек справа налево, мы можем увидеть следующий код: Отладка для одношаговой отладки, мы можем видеть, что наш стек вызовов сначала войдет в GetC (), а затем в GetB ( ) и, наконец, введите GetA ().
С точки зрения отладки кода и программ, нам не нужно обращать слишком много внимания на порядок передачи параметров и очистки стека, потому что это определяется компилятором, и мы не можем его изменить. Но третий момент часто сбивает нас с толку, потому что, если мы не понимаем этот момент, часто возникают необъяснимые ошибки при вызове и использовании друг друга между несколькими библиотеками (такими как dll, lib, exe). Я подробно расскажу об этом в следующих главах.
Особенности __stdcall
Особенности __fastcall
__thiscall
подводить итоги
Вот краткое изложение различий между _cdecl, _stdcall и __fastcall:
| Основные положения | __cdecl | __stdcall | __fastcall |
|---|---|---|---|
| Метод передачи параметров | Правша> левый | Правша> левый | Два параметра, не превышающие 4 байта (DWORD), начиная слева, помещаются в регистры ECX и EDX, а оставшиеся параметры передаются справа налево и передаются в стек |
| Очистить стек | Уборка номера | Вызывается функция очистки | Вызывается функция очистки |
| Приложения | Режим по умолчанию C / C ++ и MFC; используется, когда параметры являются переменными; | Win API | Высокая скорость |
| Соглашение о модификации компиляции Си | _functionname | [email protected] | @[email protected] |
Перепечатано в:https://blog.csdn.net/luoweifu/article/details/52425733
Fastcall c что это
Что значат __cdecl, __fastcall, и __stdcall специфики функций?
Объясните кто-нибудь понятным языком, что они делают, для чего были внедрены и где и зачем их применять?
А то чтото MSDN очень лаконичен.
| | От: | Atilla |
| Дата: | 24.12.02 07:58 | |
| Оценка: |
Здравствуйте, LaFlour, Вы писали:
LF>Что значат __cdecl, __fastcall, и __stdcall специфики функций?
LF>Объясните кто-нибудь понятным языком, что они делают, для чего были внедрены и где и зачем их применять?
LF>А то чтото MSDN очень лаконичен.
| | От: | Снорк |
| Дата: | 24.12.02 07:59 | |
| Оценка: |
Здравствуйте, LaFlour, Вы писали:
LF>Что значат __cdecl, __fastcall, и __stdcall специфики функций?
LF>Объясните кто-нибудь понятным языком, что они делают, для чего были внедрены и где и зачем их применять?
LF>А то чтото MSDN очень лаконичен.
Эти спецификаторы определяют порядок следования аргументов на стеке и сторону, которая стек очищает (caller или callee).
| | От: | Bell |
| Дата: | 24.12.02 08:04 | |
| Оценка: |
Здравствуйте, LaFlour, Вы писали:
LF>Что значат __cdecl, __fastcall, и __stdcall специфики функций?
LF>Объясните кто-нибудь понятным языком, что они делают, для чего были внедрены и где и зачем их применять?
LF>А то чтото MSDN очень лаконичен.
LF>
Это есть так называемые calling convention, т.е. способы вызова функций (вернее способ передачи параметров):
как известно, перед вызовом функции ее параметры складываются в стэк
__cdecl — после вызова функции параметры из стэка удаляет вызывающая сторона
__stdcall — после вызова функции параметры из стэка удаляет сама эта функция
__fastcall — похожа на __stdcall, но часть параметров пытается передать через регистры.
| | От: | Atilla |
| Дата: | 24.12.02 08:06 | |
| Оценка: |
Здравствуйте, Atilla, Вы писали:
Для чего используется ключевое слово fastcall в visual c?
Я видел fastcall нотация добавлена перед многими функциями. Почему он используется?
2 ответов
что записи, функция называется «конвенция.»Он указывает, как (на низком уровне) компилятор передаст входные параметры функции и получит ее результаты после ее выполнения.
но тогда как насчет заказа? Вы толкаете их слева направо или справа налево? Как насчет результата-всегда есть только один (при условии отсутствия ссылочных параметров), поэтому вы помещаете результат в стек, в зарегистрироваться по определенному адресу памяти?
что-то важное, чтобы иметь в виду: не добавлять или изменять соглашения о вызовах, если вы не знаете ровно что вы делаете, потому что, если и вызывающий абонент и вызываемый абонент не согласны с соглашением о вызове, вы, вероятно, в конечном итоге с коррупцией стека и segfault. Это обычно происходит, когда у вас есть функция, вызываемая в DLL / общей библиотеке, и пишется программа, которая зависит от DLL / SO/dylib, являющегося определенным соглашением о вызове (скажем, cdecl ), затем библиотека перекомпилируется с другим соглашением о вызовах (скажем, fastcall ). Теперь старая программа больше не может общаться с новой библиотекой.
соглашения под названием fastcall не были стандартизированы и были реализованы по-разному, в зависимости от поставщика компилятора. Обычно соглашения о вызовах fastcall передают один или несколько аргументов в регистрах, что уменьшает количество обращений к памяти, необходимых для вызова.
Русские Блоги
Соглашения о вызовах __cdecl, __stdcall и __fastcall
Что такое соглашение о вызовах
функцияСоглашение о вызовахКак следует из названия, это ограничение и спецификация (спецификация) для вызовов функций, описывающая, как передаются параметры функций и кто очищает стек. Он определяет следующее: (1) порядок размещения параметров функции, (2) извлекает ли вызывающий или вызываемый объект параметры из стека и (3) метод, который генерирует измененное имя функции.
Историческая справка
До появления микрокомпьютеров производители компьютеров почти предоставили операционную систему и компиляторы, написанные для разных языков программирования. Соглашения о вызовах, используемые платформой, определяются программной реализацией поставщика. До появления Apple early ранние микрокомпьютеры были почти полностью «голыми», и была одна ОС или компилятор, даже IBM PC. Единственный стандарт оборудования для компьютеров, совместимых с IBM PC, был определен процессорами Intel (8086, 80386) и распространен IBM. Аппаратные расширения и все программные стандарты (соглашения о вызовах BIOS) открыты для конкуренции на рынке. Группа независимых компаний-разработчиков программного обеспечения предоставляет операционные системы, компиляторы для разных языков и некоторое прикладное программное обеспечение. Исходя из разных потребностей, исторических практик и творческого подхода разработчиков, все эти компании используют разные соглашения о вызовах, которые часто сильно различаются. После перетасовки рынка совместимых машин IBM доминировали операционные системы и инструменты программирования Microsoft (с различными соглашениями о вызовах). В это время компании второго уровня, такие как Borland и Novell, и проекты с открытым исходным кодом, такие как GCC, также сохранили свои собственные Ваши собственные стандарты. Правила совместимости в конечном итоге были приняты поставщиками оборудования и программными продуктами, что упростило задачу выбора допустимого стандарта.
Уборка номера
В этих соглашениях вызывающая сторона очищает аргументы в стеке, что позволяет реализовать списки переменных параметров, таких как printf ().
cdecl
cdecl (объявление C)Язык CСоглашение о вызовах также является стандартом де-факто для C. В архитектуре x86 его содержимое включает в себя:
Visual C++Определяет возвращаемое значение функции, если оноЗначение PODИ если длина не превышает 32 бита, она передается в регистр EAX, в диапазоне 33-64 бита она передается в регистр EAX: EDX, если длина превышает 64 бита или значение не POD, вызывающая сторона заранее выделяет пространство для возвращаемого значения функции, Адрес пространства передается как неявный параметр вызываемой функции.
GCCВозвращаемое значение функции выделяется вызывающей стороной, а адрес пространства передается в качестве неявного параметра вызываемой функции без использования регистра EAX. Начиная с версии 4.5 GCC, при вызове функций данные в стеке должны быть выровнены в 16B (в предыдущих версиях требовалось только 4B выравнивания).
Рассмотрим следующий фрагмент кода C:
На x86 создается следующий код сборки (синтаксис AT & T):
После возврата из функции вызываемая функция очищает стек. Есть некоторые различия в понимании cdecl, особенно в том, как возвращать значения. В результате программы x86 становятся несовместимыми после компиляции разными компиляторами на разных платформах ОС, даже если они используют правило «cdecl» и не используют системные вызовы. Некоторые компиляторы возвращают простые структуры данных, которые занимают приблизительно два регистра и помещаются в пару регистров EAX: EDX; более крупные структуры и объекты класса требуют некоторой специальной обработки обработчиком исключений (например, определенный конструктор, анализ Конструктор или присваивание), хранящиеся в памяти. Чтобы поместить его в память, вызывающей стороне необходимо выделить некоторую память и позволить указателю указать на эту память, этот указатель используется в качестве первого скрытого параметра, вызываемый использует эту память и возвращает всплывающие указатели при возврате. Скрытый указатель В Linux / GCC значения с плавающей точкой помещаются в стек через псевдостек x87. Как это:
Использование этого метода гарантирует, что стек может быть передан в правильном формате. Соглашение о вызовах cdecl обычно используется в качестве правила вызова по умолчанию для компилятора x86 C. Многие компиляторы также предоставляют возможность автоматического переключения соглашения о вызовах. Если вам нужно вручную указать правило вызова как cdecl, компилятор может поддерживать следующий синтаксис:
Модификатор _cdecl должен быть указан в прототипе функции, а другие параметры будут перезаписаны в объявлении функции.
syscall
Подобно cdecl, параметры помещаются в стек справа налево. EAX, ECX и EDX не сохраняют значения. Размер списка параметров помещается в регистр AL (?). syscall является стандартом для 32-битного OS / 2 API.
optlink
Уборка улиц
Если вызываемый объект хочет очистить параметры в стеке, он должен знать, сколько байтов в стеке нужно обработать на этапе компиляции. Поэтому этот тип соглашения о вызовах не совместим со списками переменных параметров, такими как printf (). Однако это соглашение о вызовах может быть более эффективным, потому что код, который необходимо разложить, не должен генерироваться при каждом вызове. Функции, использующие это правило, легко распознаются в коде asm, потому что перед возвращением они разбивают стек. Инструкция x86 ret позволяет опциональному 16-битному параметру указать количество байтов стека, используемых для разборки стека перед возвратом его вызывающей стороне. Код выглядит так:
pascal
Основываясь на соглашении о вызовах языка Pascal, параметры помещаются в стек слева направо (в отличие от cdecl). Вызываемый отвечает за очистку стека перед возвратом. Это соглашение о вызовах обычно встречается в компиляторах для следующих 16-битных платформ: OS / 2 1.x, Microsoft Windows 3.x и Borland Delphi версии 1.x.
register
Псевдоним для быстрого вызова Borland.
stdcall
Инструменты компиляции Microsoft предусматривают: PASCAL, WINAPI, APIENTRY, FORTRAN, CALLBACK, STDCALL, __far __pascal, __fortran, __stdcall Все относятся к таким соглашениям о вызовах.
fastcall
Это соглашение не было стандартизировано, и реализации не согласованы между компиляторами.
Соглашение __fastcall от Microsoft или GCC (то есть __msfastcall) передает первый (слева направо) параметр, который не превышает 32 бита, через регистр ECX / CX / CL, а второй параметр, который не превышает 32 бита, через регистр EDX. / DX / DL, остальные параметры помещаются в стек в порядке справа налево.
Слева направо передайте три параметра в EAX, EDX и ECX. Остальные параметры помещаются в стек слева направо. В 32-разрядном компиляторе Embarcadero Delphi это соглашение о вызовах по умолчанию, которое в компиляторе называется регистром. Некоторые версии на i386LinuxЭто соглашение также используется.
Вызов вызывающего абонента
thiscall
призваниеC++Используйте это соглашение для нестатических функций-членов. Существует две основные версии этого вызова, основанные на том, являются ли используемые компилятор и функция varargs. Для компилятора GCC этот вызов почти эквивалентен cdecl: вызывающая сторона очищает стек, а параметры передаются справа налево. Разница заключается в указателе this, thiscall помещает указатель this в стек в конце, что эквивалентно неявному параметру first-left в прототипе функции.
вMicrosoftVisual C++составительВ этом указатель this передается через регистр ECX, а остальное такое же, как в соглашении cdecl. Когда функция принимает переменный аргумент, вызывающая сторона отвечает за очистку стека (см. Cdecl). Это соглашение о вызовах явно указано только в Microsoft Visual C ++ 2005 и более поздних версиях. В других компиляторах thiscall не является ключевым словом (дизассемблеры, такие как IDA, используют __thiscall).
Intel ABI
Согласно Intel ABI, EAX, EDX и ECX могут свободно использоваться в процедурах или функциях и не требуют сохранения.
Соглашение о вызовах x86-64
Соглашение о вызовах x86-64 имеет больше регистров, которые можно использовать для передачи параметров. Более того, меньше несовместимых соглашений о вызовах, но есть еще два основных правила.
Соглашение о вызовах Microsoft x64
Например, функция имеет 5 целочисленных параметров, с первого по четвертый помещаются в регистры, а пятый помещается в верхнюю часть стека за пределами теневого пространства. Когда функция вызывается, этот стек используется для составления возвращаемого значения теневого пространства 32 бита + пятый параметр.
В архитектуре x86-64 Visual Studio 2008 хранит числа с плавающей запятой в XMM6 и XMM7 (также от XMM8 до XMM15). В результате для подпрограмм на языке ассемблера, написанных пользователем, XMM6 и XMM7 должны быть сохранены (эти два регистра не требуются для x86), что означает, что при переносе подпрограмм на ассемблере между x86 и x86-64 необходимо обратить внимание на / После этого XMM6 и XMM7 должны быть сохранены / восстановлены.
System V AMD64 ABI
Это соглашение в основном используется в Solaris, GNU / Linux, FreeBSD и других ОС, не относящихся к Microsoft. Первые шесть целочисленных параметров помещаются в регистры RDI, RSI, RDX, RCX, R8 и R9, в то же время от XMM0 до XMM7 используются для размещения аргументов с плавающей запятой. Для системных вызовов вместо RCX используется R10. Как и в соглашении Microsoft x64, другие дополнительные параметры помещаются в стек, а возвращаемое значение сохраняется в RAX. В отличие от Microsoft, нет необходимости предоставлять теневое пространство. При входе в функцию возвращаемое значение находится рядом с седьмым целочисленным параметром в стеке.
Мы знаем, что функция состоит из следующих частей: имя функции типа возвращаемого значения (список параметров), например:
【code1】
Вышеприведенный компонент является хорошо известным компонентом, на самом деле есть еще одна часть состава функции, то есть соглашение о вызовах. Следующим образом:
【code2】
Декларация и определение соглашения о вызовах должны быть одинаковыми
В VC ++ соглашение о вызовах является частью типа функции,Следовательно, соглашение о вызовах при объявлении и определении функции должно быть одинаковым. Соглашение о вызовах не может быть только в объявлении, и в определении нет или другое определение.Следующим образом:
[code3] Неправильно используйте один:
Сообщение об ошибке:
error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__stdcall *)(int,int)’ to ‘int’
Выше хорошо, потому что по умолчанию __cdecl.
[Code4] Неправильное использование 2:
Сообщение об ошибке:
error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__cdecl *)(int,int)’ to ‘int’
[Code5] Неправильное использование три:
Сообщение об ошибке:
error C2373: ‘add’: redefinition; different type modifiers
error C2440: ‘initializing’: cannot convert from ‘int (__stdcall *)(int,int)’ to ‘int’
[Code6] Правильное использование:
Вызов функции
Процесс вызова функции может быть описан следующим образом:
(1) Сначала поместите базовый адрес (ebp) стека вызывающего абонента (A) в стек, чтобы сохранить информацию предыдущей задачи.
(2) Затем присвойте значение верхнего указателя (esp) вызывающей стороны (A) для ebp в качестве нового базового адреса (то есть нижней части стека вызываемой стороны B).
(3) Затем откройте (обычно подинструкции) соответствующее пространство на этом базовом адресе (в нижней части стека вызываемого B) как пространство стека вызываемого B.
(4) После возврата из функции B ebp из текущего кадра стека восстанавливается до верхней части вызывающей стороны A (esp), так что вершина стека восстанавливает позицию до вызова функции B, затем Вызывающий объект A может вытолкнуть предыдущее значение ebp с вершины восстановленного стека (это можно сделать, потому что это значение помещается в стек перед вызовом функции). Таким образом, ebp и esp оба восстанавливают позицию перед вызовом функции B, то есть стек восстанавливает состояние до вызова функции B.
Этот процесс выполняется в сборке AT & T с помощью двух инструкций, а именно:
__cdecl Особенности
(1). Чтобы убедиться, что параметры помещены в стек справа налево, мы можем взглянуть на следующий код: Отладка для одношаговой отладки, вы можете видеть, что наш стек вызовов сначала вводит GetC (), затем GetB ( ) И, наконец, введите GetA ().
С точки зрения отладки кода и программ нам не нужно обращать внимание на порядок добавления стека и очистки стека, потому что это определяется компилятором, и мы не можем его изменить. Но третий момент часто беспокоит нас, потому что, если мы этого не понимаем, часто возникают необъяснимые ошибки, когда несколько библиотек (таких как dll, lib, exe) вызывают и зависят друг от друга. Я подробно расскажу об этом в следующих главах.
__stdcall Особенности
__fastcall Особенности
__thiscall
резюме
Вот краткое изложение различий между _cdecl, _stdcall и __fastcall:
__cdecl
| Элемент | Реализация |
|---|---|
| Порядок передачи аргументов | Справа налево. |
| Обязанность по обслуживанию стека | Вызывающая функция выводит аргументы из стека. |
| Соглашение об оформлении имен | Перед именами ставится символ подчеркивания (_), кроме случаев экспорта функций __cdecl, использующих компоновку С. |
| Соглашение о преобразовании регистра | Изменение регистра не выполняется. |
Связанные сведения см. в разделе декорированные имена.
Поместите __cdecl модификатор перед именем переменной или функции. Так как соглашения об именовании и вызове C являются стандартными, единственное время, которое необходимо использовать __cdecl в коде x86, — это только в том случае, если вы указали /Gv параметр компилятора (векторкалл), /Gz (STDCALL) или /Gr (fastcall). Параметр компилятора /GD вызывает принудительное соглашение о вызовах.
В процессорах ARM и x64 __cdecl принимается, но обычно игнорируется компилятором. По соглашению на ARM и x64 аргументы передаются в регистрах, когда это возможно, а последующие аргументы передаются в стек. В коде x64 используйте __cdecl для переопределения параметра компилятора __cdecl и используйте соглашение о вызовах по умолчанию x64.
Если используется внестрочное определение нестатической функции класса, то модификатор соглашения о вызовах не должен быть задан во внестрочном определении. То есть для нестатических методов-членов считается, что соглашение о вызовах, указанное во время объявления, было сделано в точке определения. Рассмотрим следующее определение класса:
В этом случае следующий код:
Пример
В следующем примере компилятору дается инструкция использовать для функции system соглашения об именовании и вызовах C: