Что такое значение и использование stdcall?

я наткнулся __stdcall много в эти дни.

MSDN не очень ясно объясняет, что это на самом деле означает, когда и почему он должен использоваться, если вообще.

Я был бы признателен, если бы кто-нибудь дал объяснение, желательно с примером или двумя.

9 ответов


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

наиболее популярными соглашениями о вызовах в windows являются

  • нарушением соглашения о стандартном
  • ключевое слово cdecl
  • clrcall
  • fastcall
  • thiscall

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

соглашения о вызовах описаны здесь

Раймонд Чен также сделал длинную серию по истории различных соглашений о вызовах (5 частей), начиная здесь.


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

/* example of __cdecl */
push arg1
push arg2
push arg3
call function
add sp,12 // effectively "pop; pop; pop"

Примечание: соглашение по умолчанию-показано выше-известно как _ _ cdecl.

другим наиболее популярным соглашением является __stdcall. В нем параметры снова выталкиваются вызывающим, но стек очищается вызываемым. Это стандартное соглашение для Win32 API функции (как определено макросом WINAPI В ), и это также иногда называют соглашением о вызове "Pascal".

/* example of __stdcall */
push arg1 
push arg2 
push arg3 
call function // no stack cleanup - callee does this

это выглядит как незначительная техническая деталь, но если есть разногласия о том, как стек управляется между вызывающим и вызываемым, стек будет уничтожен таким образом, что вряд ли будет восстановлен. Поскольку __stdcall выполняет очистку стека, (очень маленький) код для выполнения этой задачи находится только в одном месте, а не дублируется в каждом вызывающем объекте как и в _ _ cdecl. Это делает код немного меньше, хотя влияние размера видно только в больших программах.

вариационные функции, такие как printf (), почти невозможно получить с __stdcall, потому что только вызывающий действительно знает, сколько аргументов было передано, чтобы очистить их. Вызываемый может сделать некоторые хорошие догадки (скажем, глядя на строку формата), но очистка стека должна быть определена фактической логикой функции, а не призыв-сам механизм конвенции. Следовательно, только __cdecl поддерживает variadic функции, чтобы вызывающий объект мог выполнить очистку.

украшения имени символа компоновщика: Как упоминалось выше, вызов функции с" неправильным " соглашением может быть катастрофическим, поэтому у Microsoft есть механизм, чтобы избежать этого. Это работает хорошо, хотя это может сводить с ума, если кто-то не знает, каковы причины. Они выбрали для решения этой кодирования вызове в низкоуровневые имена функций с дополнительными символами (которые часто называются "украшения"), и они рассматриваются как несвязанные имена, линкер. Соглашение о вызове по умолчанию - __cdecl, но каждый из них может быть запрошен явно с помощью /G? параметр для компилятора.

_ _ cdecl (cl / Gd ...)

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

_ _ stdcall (cl / Gz ...)

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

_ _ fastcall (cl / Gr ...)

эти имена функций начинаются со знака @ и имеют суффикс с @ parameter count, как __stdcall.

примеры:

Declaration                        ----------------------->    decorated name


void __cdecl foo(void);            ----------------------->    _foo

void __cdecl foo(int a);           ----------------------->    _foo

void __cdecl foo(int a, int b);    ----------------------->    _foo

void __stdcall foo(void);          ----------------------->    _foo@0

void __stdcall foo(int a);         ----------------------->    _foo@4

void __stdcall foo(int a, int b);  ----------------------->    _foo@8

void __fastcall foo(void);         ----------------------->    @foo@0

void __fastcall foo(int a);        ----------------------->    @foo@4

void __fastcall foo(int a, int b); ----------------------->    @foo@8

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

Раймонд Чен написал блог о основных соглашениях о вызовах x86, и есть хороший статья CodeProject тоже.

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


к сожалению, нет простого ответа, когда его использовать, а когда нет.

__stdcall означает, что аргументы функции помещаются в стек с первого по последний. Это в отличие от __cdecl, что означает, что аргументы передаются от последнего к первому, и __fastcall, который помещает первые четыре (я думаю) аргумента в регистры, а остальные идут в стек.

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


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


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

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


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

Если вызывающий и вызываемый код используют разные соглашения, вы сталкиваетесь с неопределенным поведением (например,такая странная авария).

компиляторы C++ по умолчанию не используют __stdcall - они используют другие соглашения. Так по порядку чтобы вызвать функции WinAPI из C++, вам нужно указать, что они используют __stdcall-это обычно делается в заголовочных файлах WINDOES SDK, и вы также делаете это при объявлении указателей функций.


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

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

в противном случае function-callee и function-caller могут используйте различные соглашения, вызывающие сбой программы.


_ _ stdcall - это соглашение о вызовах для функции. Это сообщает компилятору правила, которые применяются для настройки стека, нажатия аргументов и получения возвращаемого значения. Существует ряд других соглашений о вызовах, таких как _ _ cdecl, __thiscall, _ _ fastcall и _ _ naked.

_ _ stdcall является стандартным соглашением о вызовах для системных вызовов Win32.

больше подробности можно найти на Википедия.