102 votes

stdcall et cdecl

Il existe deux types de conventions d'appel - stdcall et cdecl. J'ai quelques questions sur eux.

  1. Lorsqu'un cdecl fonction est appelée par l'appelant, comment un appelant savoir si elle doit se libérer de la pile ? Sur le site d'appel, ne l' appelant savoir si la fonction appelée est un cdecl ou un stdcall la fonction ? Comment ça fonctionne ? Comment savoir à la cliente si elle doit gratuit haut de la pile ou pas ? Ou est-ce le linkers responsabilité ?
  2. Si une fonction est déclarée comme stdcall appelle une fonction(qui a une convention d'appel comme cdecl), ou l'inverse, serait cette inapproprié ?
  3. En général, nous pouvons dire que l'appel sera plus rapide - cdecl ou stdcall ?

90voto

In silico Points 30778

Raymond Chen donne un bel aperçu de ce qu' __stdcall et __cdecl n'.

a) L'appelant "sait" pour nettoyer la pile après l'appel d'une fonction, le compilateur sait que la convention d'appel de cette fonction et génère le code nécessaire.

void __stdcall StdcallFunc() {}

void __cdecl CdeclFunc()
{
    // The compiler knows that StdcallFunc() uses the __stdcall
    // convention at this point, so it generates the proper binary
    // for stack cleanup.
    StdcallFunc();
}

Il est possible d'incompatibilité de la convention d'appel, comme ceci:

LRESULT MyWndProc(HWND hwnd, UINT msg,
    WPARAM wParam, LPARAM lParam);
// ...
// Compiler usually complains but there's this cast here...
windowClass.lpfnWndProc = reinterpret_cast<WNDPROC>(&MyWndProc);

Donc, de nombreux exemples de code se tromper, il n'est même pas drôle. C'est censé être comme ceci:

// CALLBACK is #define'd as __stdcall
LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg
    WPARAM wParam, LPARAM lParam);
// ...
windowClass.lpfnWndProc = &MyWndProc;

Cependant, en supposant que le programmeur n'a pas ignorer les erreurs de compilation, le compilateur va générer le code nécessaire pour nettoyer la pile correctement, puisqu'il sera connaître les conventions d'appel de fonctions impliquées.

b) les Deux façons devrait fonctionner. En fait, cela arrive assez souvent, au moins dans le code qui interagit avec l'API Windows, car __cdecl est la valeur par défaut pour les programmes C et C++ selon le compilateur Visual C++ et la WinAPI des fonctions utilisation de l' __stdcall convention.

c) Il devrait y avoir pas de réelle différence de performance entre les deux.

49voto

adf88 Points 2347

Dans CDECL arguments sont poussés sur la pile au revers de l'ordre, l'appelant efface la pile et le résultat est renvoyé via le processeur de registre (plus tard, je vais l'appeler "s'inscrire"). Dans STDCALL il y a une différence, l'appelant doeasn pas clair de la pile, la calle faire.

Vous vous demandez lequel est le plus rapide. Personne n'. Vous devez utiliser natif convention d'appel, aussi longtemps que vous le pouvez. Convention sur les changements que si il n'y a pas moyen de sortir, lors de l'utilisation de bibliothèques externes qui requiert une certaine convention à être utilisé.

En outre, il existe d'autres conventions que le compilateur peut choisir en tant que par défaut c'est à dire au compilateur Visual C++ utilise FASTCALL, qui est théoriquement plus rapide en raison de plus de l'utilisation extensive de registres du processeur.

Habituellement, vous devez donner un bon de convention d'appel de signature pour les fonctions de callback passé pour certains de bibliothèque externe c'est à dire de rappel à l' qsort à partir de la bibliothèque C doit être CDECL (si le compilateur par défaut utilise l'autre convention, alors nous devons marquer le rappel que CDECL) ou les diverses WinAPI rappels doivent être STDCALL (toute la WinAPI est STDCALL).

D'autres cas d'habitude peut-être quand vous êtes stocker des pointeurs vers certaines fonctions externes c'est à dire de créer un pointeur vers WinAPI fonction de son type de définition doivent être marqués avec STDCALL.

Et ci-dessous est un exemple montrant comment le compilateur de le faire:

/* 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 copy of 'y', then 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 pseaudo-assembler */
/* Now copies push onto the stack are 'a', 'b' and 'c' variables */
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, result 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 copy of 'y', then 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)

16voto

Lee Hamilton Points 51

J'ai remarqué une annonce qui disent qu'il n'a pas d'importance si vous appelez un __stdcall d'un __cdecl ou vice versa. Il n'.

La raison: avec __cdecl les arguments qui sont passés à l'appelées les fonctions sont retiré de la pile par l'appel de la fonction, en __stdcall, les arguments sont retiré de la pile par la fonction appelée. Si vous appelez un __cdecl fonction __stdcall, la pile n'est pas nettoyé à tous, donc finalement, lorsque l' __cdecl utilise un empilées base de référence pour les arguments ou de l'adresse de retour vous sera d'utiliser les anciennes données au cours actuel du pointeur de pile. Si vous appelez un __stdcall fonction d'un __cdecl, __stdcall fonction nettoie les arguments sur la pile, puis l' __cdecl fonction-t-il de nouveau, éventuellement, retirer l'appel de fonctions de retour d'informations.

Microsoft convention pour C essaie de contourner cela en mangling les noms. Un __cdecl fonction est préfixé par un trait de soulignement. Un __stdcall fonction des préfixes avec un trait de soulignement et suffixé par un signe "@" et le nombre d'octets à être supprimé. Par exemple __cdecl f(x) est lié en tant que _x, __stdcall f(int x) est lié _f@4sizeof(int) 4 octets)

Si vous parvenez à obtenir au-delà de l'éditeur de liens, de profiter de la débogage mess.

2voto

Puppy Points 90818

Il est spécifié dans le type de fonction. Lorsque vous avez un pointeur de fonction, il est supposé pour être cdecl si pas explicitement stdcall. Cela signifie que si vous obtenez un stdcall pointeur et un cdecl pointeur, vous ne pouvez pas les échanger. Les deux types de fonctions peuvent s'appeler les uns les autres sans problèmes, c'est juste l'obtention d'un type lorsque vous s'attendre à l'autre. Comme pour la vitesse, ils exercent les mêmes rôles, juste très légèrement différent, c'est vraiment hors de propos.

1voto

sharptooth Points 93379

L'appelant et l'appelé doivent utiliser la même convention, au point de invokation - c'est la seule manière fiable. À la fois de l'appelant et de l'appelé suivre un protocole prédéfini par exemple, qui a besoin de nettoyer la pile. Si les conventions de l'incompatibilité de votre programme s'exécute dans un comportement non défini - probablement juste se bloque de façon spectaculaire.

Ce n'est requis par invokation site - le code appelant lui-même peut être une fonction avec toute convention d'appel.

Vous ne devriez pas remarquer une différence de performance entre ces conventions. Si cela devient un problème, vous avez généralement besoin de faire moins d'appels - par exemple, le changement de l'algorithme.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X