Co to jest __stdcall?


151

Uczę się programowania w Win32, a WinMainprototyp wygląda tak:

int WINAPI WinMain ( HINSTANCE instance, HINSTANCE prev_instance, PSTR cmd_line, int cmd_show )

Nie wiedziałem, do czego WINAPIsłuży ten identyfikator, i znalazłem:

#define WINAPI      __stdcall

Co to robi? Jestem zdezorientowany tym, że mam coś po typie powrotu. Do czego służy __stdcall? Co to znaczy, że istnieje coś między typem zwracanym a nazwą funkcji?


2
@AndrewProck „Fikcyjna” zmiana była w tym przypadku w porządku, ale ogólnie można użyć  s, aby obejść głupie (i nieproduktywne - w tym przypadku) minimum 6 znaków.
Adi Inbar,

Odpowiedzi:


170

__stdcalljest konwencją wywoływania używaną dla funkcji. Informuje to kompilator o zasadach, które mają zastosowanie do konfiguracji stosu, wypychania argumentów i pobierania wartości zwracanej.

Istnieje szereg innych konwencji telefonicznych, __cdecl, __thiscall, __fastcalli cudownie nazwie __declspec(naked). __stdcalljest standardową konwencją wywoływania wywołań systemowych Win32.

Wikipedia zawiera szczegóły .

Ma to przede wszystkim znaczenie, gdy wywołujesz funkcję poza swoim kodem (np. API systemu operacyjnego) lub system operacyjny woła Cię (jak w przypadku WinMain). Jeśli kompilator nie zna prawidłowej konwencji wywoływania, prawdopodobnie wystąpią bardzo dziwne awarie, ponieważ stos nie będzie zarządzany poprawnie.


8
Zobacz to pytanie, aby
zapoznać

Jeśli się nie mylę, te konwencje wywoływania kontrolują sposób, w jaki kompilator tworzy kod asemblera. Podczas interakcji z kodem asemblera ważne jest, aby wziąć pod uwagę konwencję wywoływania, aby zapobiec problemom ze stosem. Jest tu ładna tabela dokumentująca niektóre konwencje: msdn.microsoft.com/en-us/library/984x0h58.aspx
Nicholas Miller

Czy możemy powiedzieć, że spowodowałoby to wyłączenie niektórych nielegalnych optymalizacji?
kesari,

40

Same C lub C ++ nie definiują tych identyfikatorów. Są rozszerzeniami kompilatora i reprezentują określone konwencje wywoływania. To określa, gdzie umieścić argumenty, w jakiej kolejności, gdzie wywoływana funkcja znajdzie adres zwrotny i tak dalej. Na przykład __fastcall oznacza, że ​​argumenty funkcji są przekazywane przez rejestry.

Wikipedia Artykuł zawiera przegląd różnych konwencjach telefonicznych znajdujących się tam.


18

Odpowiedzi do tej pory obejmowały szczegóły, ale jeśli nie zamierzasz schodzić do asemblera, wszystko, co musisz wiedzieć, to to, że zarówno dzwoniący, jak i wywoływany muszą używać tej samej konwencji wywoływania, w przeciwnym razie pojawią się błędy, które są trudne do znalezienia.


12

Zgadzam się, że wszystkie dotychczasowe odpowiedzi są poprawne, ale oto powód. Kompilatory C i C ++ firmy Microsoft zapewniają różne konwencje wywoływania dla (zamierzonej) szybkości wywoływania funkcji w ramach funkcji C i C ++ aplikacji. W każdym przypadku dzwoniący i odbierający muszą uzgodnić, której konwencji dzwonienia użyć. Teraz sam Windows dostarcza funkcje (API), a te zostały już skompilowane, więc kiedy je wywołujesz, musisz się do nich dostosować. Wszelkie wywołania interfejsów API systemu Windows i wywołania zwrotne z interfejsów API systemu Windows muszą używać konwencji __stdcall.


3
i nie można go mylić z _standard_call, ponieważ jest to standardowe-c! można by pomyśleć, że to byłby punkt __stdcall, gdyby ktoś nie wiedział lepiej
Johannes Schaub - litb

3
Mały dzióbek: istnieje kilka interfejsów API systemu Windows, które używają __cdecl zamiast __stdcall - zwykle te, które przyjmują zmienną liczbę parametrów, takie jak wsprintf ().
Michael Burr

Masz rację. Nazwa ma wyglądać jak funkcja CRT, ale jest to API. Czy przypadkiem wiesz, jak P / wywołać to z C #?
Programista Windows,

Nie testowałem tego, ale pinvoke.net daje taki podpis: "static extern int wsprintf ([Out] StringBuilder lpOut, string lpFmt, ...);"
Michael Burr

Moja intuicja mówi, że kompilator C # nie wiedziałby, jak używać konwencji __cdecl na tym.
Programista Windows



4

__stdcall służy do umieszczania argumentów funkcji na stosie. Po zakończeniu funkcji automatycznie zwalnia pamięć. Jest to używane dla ustalonych argumentów.

void __stdcall fnname ( int, int* )
{
    ...
}

int main()
{
    CreateThread ( NULL, 0, fnname, int, int*...... )
}

Tutaj fnname ma argumenty, które są umieszczane bezpośrednio na stosie.


1

Nigdy wcześniej nie musiałem tego używać, aż do dzisiaj. Dzieje się tak, ponieważ w moim kodzie używam wielowątkowości, a wielowątkowość API, której używam, to Windows (_beginthreadex).

Aby rozpocząć wątek:

_beginthreadex(NULL, 0, ExecuteCommand, currCommand, 0, 0);

Funkcja ExecuteCommand MUSI użyć słowa kluczowego __stdcall w sygnaturze metody, aby beginthreadex mógł ją wywołać:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.