O que é __stdcall?


151

Estou aprendendo sobre programação Win32, e o WinMainprotótipo se parece com:

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

Fiquei confuso sobre o que WINAPIera esse identificador e encontrei:

#define WINAPI      __stdcall

O que isso faz? Estou confuso por ter alguma coisa depois de um tipo de retorno. Para que serve __stdcall? O que significa quando há algo entre o tipo de retorno e o nome da função?


2
@AndrewProck A alteração "fictícia" foi aceitável neste caso, mas em geral você pode usar  s para contornar o mínimo bobo (e contraproducente - neste caso) de 6 caracteres.
Adi Inbar

Respostas:


170

__stdcallé a convenção de chamada usada para a função. Isso informa ao compilador as regras que se aplicam para configurar a pilha, enviar argumentos e obter um valor de retorno.

Há uma série de outras convenções de chamada, __cdecl, __thiscall, __fastcalle maravilhosamente nomeado __declspec(naked). __stdcallé a convenção de chamada padrão para chamadas do sistema Win32.

A Wikipedia cobre os detalhes .

É importante principalmente quando você está chamando uma função fora do seu código (por exemplo, uma API do sistema operacional) ou se o sistema operacional está chamando você (como é o caso do WinMain). Se o compilador não souber a convenção de chamada correta, provavelmente ocorrerá falhas muito estranhas, pois a pilha não será gerenciada corretamente.


8
Veja esta pergunta para um exemplo de acidentes muito starnge stackoverflow.com/questions/696306/...
sharptooth

Se não me engano, essas convenções de chamada controlam como o compilador produz o código do assembly. Ao fazer interface com o código do assembly, é essencial que a convenção de chamada seja observada para evitar problemas de pilha. Há uma mesa agradável aqui documentar algumas convenções: msdn.microsoft.com/en-us/library/984x0h58.aspx
Nicholas Miller

Podemos dizer que isso desabilitaria certas otimizações ilegais?
kesari

40

C ou C ++ em si não definem esses identificadores. Eles são extensões do compilador e representam certas convenções de chamada. Isso determina onde colocar argumentos, em que ordem, onde a função chamada encontrará o endereço de retorno e assim por diante. Por exemplo, __fastcall significa que argumentos de funções são passados ​​por registradores.

O artigo da Wikipedia fornece uma visão geral das diferentes convenções de chamadas encontradas lá.


18

As respostas até agora cobriram os detalhes, mas se você não pretende ir para a montagem, tudo o que você precisa saber é que o chamador e o destinatário devem usar a mesma convenção de chamada; caso contrário, você receberá bugs que são difíceis de encontrar.


12

Concordo que todas as respostas até agora estão corretas, mas aqui está a razão. Os compiladores C e C ++ da Microsoft fornecem várias convenções de chamada para a velocidade (pretendida) de chamadas de função nas funções C e C ++ de um aplicativo. Em cada caso, o chamador e o destinatário devem concordar com qual convenção de chamada usar. Agora, o próprio Windows fornece funções (APIs) e essas já foram compiladas; portanto, quando você as chama, deve estar em conformidade com elas. Quaisquer chamadas para APIs do Windows e retornos de chamada das APIs do Windows devem usar a convenção __stdcall.


3
e não deve ser confundido com _standard_call, pois é padrão-c! pode-se pensar que esse seria o objetivo de __stdcall se não se conhecesse melhor
Johannes Schaub - litb 18/11/08 -

3
Um pequeno detalhe: existem algumas APIs do Windows que usam __cdecl em vez de __stdcall - geralmente aquelas que recebem um número variável de parâmetros, como wsprintf ().
Michael Burr

Você está certo. É nomeado para parecer uma função CRT, mas é uma API. Por acaso, você sabe como invocá-lo em C #?
Programador Windows

Eu não testei isso, mas o pinvoke.net fornece esta assinatura: "static extern int wsprintf ([Out] StringBuilder lpOut, string lpFmt, ...);"
Michael Burr

Minha intuição diz que o compilador C # não saberia usar a convenção __cdecl nessa.
Programador Windows



4

__stdcall é usado para colocar os argumentos da função na pilha. Após a conclusão da função, ela desaloca automaticamente a memória. Isso é usado para argumentos fixos.

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

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

Aqui o nome fn tem argumentos que ele envia diretamente para a pilha.


1

Eu nunca tive que usar isso antes até hoje. É porque no meu código eu estou usando multiencadeamento e a API de multiencadeamento que eu estou usando é a do Windows (_beginthreadex).

Para iniciar o encadeamento:

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

A função ExecuteCommand DEVE usar a palavra-chave __stdcall na assinatura do método para o beginthreadex chamá-lo:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.