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?

Tristan Havelick
źródło
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.

Rob Walker
źródło
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.

Johannes Schaub - litb
źródło
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.

MovEaxEsp
źródło
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.

Programista Windows
źródło
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
5

Ma to związek ze sposobem wywoływania funkcji - w zasadzie kolejnością, w jakiej rzeczy są umieszczane na stosie i kto jest odpowiedzialny za czyszczenie.

Oto dokumentacja, ale niewiele to znaczy, chyba że rozumiesz pierwszą część:
http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx

Joel Coehoorn
źródło
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.

philippe lhardy
źródło
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));
}
Katianie
źródło