Em CDECL, os argumentos são colocados na pilha em ordem reversa, o chamador limpa a pilha e o resultado é retornado por meio do registro do processador (posteriormente chamarei de "registro A"). Em STDCALL há uma diferença, o chamador não limpa a pilha, mas a chamada faz.
Você está perguntando qual é mais rápido. Ninguém. Você deve usar a convenção de chamada nativa o máximo que puder. Altere a convenção apenas se não houver saída, ao usar bibliotecas externas que requerem o uso de certa convenção.
Além disso, existem outras convenções que o compilador pode escolher como padrão, isto é, o compilador Visual C ++ usa FASTCALL, que é teoricamente mais rápido devido ao uso mais extensivo de registros do processador.
Normalmente, você deve fornecer uma assinatura de convenção de chamada adequada para funções de retorno de chamada passadas para alguma biblioteca externa, ou seja, o retorno de chamada qsort
da biblioteca C deve ser CDECL (se o compilador por padrão usa outra convenção, então devemos marcar o retorno de chamada como CDECL) ou vários retornos de chamada WinAPI devem ser STDCALL (WinAPI inteiro é STDCALL).
Outro caso comum pode ser quando você está armazenando ponteiros para algumas funções externas, por exemplo, para criar um ponteiro para a função WinAPI, sua definição de tipo deve ser marcada com STDCALL.
E abaixo está um exemplo que mostra como o compilador faz isso:
/* 1. calling function in C++ */
i = Function(x, y, z);
/* 2. function body in C++ */
int Function(int a, int b, int c) { return a + b + c; }
CDECL:
/* 1. calling CDECL 'Function' in pseudo-assembler (similar to what the compiler outputs) */
push on the stack a copy of 'z', then a copy of 'y', then a copy of 'x'
call (jump to function body, after function is finished it will jump back here, the address where to jump back is in registers)
move contents of register A to 'i' variable
pop all from the stack that we have pushed (copy of x, y and z)
/* 2. CDECL 'Function' body in pseudo-assembler */
/* Now copies of 'a', 'b' and 'c' variables are pushed onto the stack */
copy 'a' (from stack) to register A
copy 'b' (from stack) to register B
add A and B, store result in A
copy 'c' (from stack) to register B
add A and B, store result in A
jump back to caller code (a, b and c still on the stack, the result is in register A)
STDCALL:
/* 1. calling STDCALL in pseudo-assembler (similar to what the compiler outputs) */
push on the stack a copy of 'z', then a copy of 'y', then a copy of 'x'
call
move contents of register A to 'i' variable
/* 2. STDCALL 'Function' body in pseaudo-assembler */
pop 'a' from stack to register A
pop 'b' from stack to register B
add A and B, store result in A
pop 'c' from stack to register B
add A and B, store result in A
jump back to caller code (a, b and c are no more on the stack, result in register A)