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


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

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

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

9 56

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 поддерживает вариативные функции, так что вызывающий может сделать очистку.

Компоновщик название символа украшения: Как упоминалось в пункте выше, вызов функции с" неправильным " соглашением может быть катастрофическим, поэтому у 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-это еще одно соглашение, которое используется для загрузки функции в стек или регистры.

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

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

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

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

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