Il existe (entre autres) deux types de conventions d'appel - stdcall et cdecl . J'ai quelques questions à leur sujet:
- Lorsqu'une fonction cdecl est appelée, comment un appelant sait-il s'il doit libérer la pile? Sur le site d'appel, l'appelant sait-il si la fonction appelée est une fonction cdecl ou stdcall? Comment ça marche ? Comment l'appelant sait-il s'il doit libérer la pile ou non? Ou est-ce la responsabilité des linkers?
- Si une fonction déclarée comme stdcall appelle une fonction (qui a une convention d'appel comme cdecl), ou l'inverse, serait-ce inapproprié?
- En général, pouvons-nous dire que quel appel sera plus rapide - cdecl ou stdcall?
Réponses:
Raymond Chen donne un bon aperçu de ce
__stdcall
que__cdecl
fait et fait .(1) L'appelant «sait» nettoyer la pile après avoir appelé une fonction car le compilateur connaît la convention d'appel de cette fonction et génère le code nécessaire.
Il est possible de ne pas correspondre à la convention d'appel , comme ceci:
Tant d'échantillons de code se trompent, ce n'est même pas drôle. C'est censé être comme ça:
Cependant, en supposant que le programmeur n'ignore pas les erreurs du compilateur, le compilateur générera le code nécessaire pour nettoyer correctement la pile car il connaîtra les conventions d'appel des fonctions impliquées.
(2) Les deux méthodes devraient fonctionner. En fait, cela se produit assez fréquemment au moins dans le code qui interagit avec l'API Windows, car
__cdecl
c'est la valeur par défaut pour les programmes C et C ++ selon le compilateur Visual C ++ et les fonctions WinAPI utilisent la__stdcall
convention .(3) Il ne devrait pas y avoir de réelle différence de performance entre les deux.
la source
Dans CDECL, les arguments sont poussés sur la pile dans l'ordre inverse, l'appelant efface la pile et le résultat est renvoyé via le registre du processeur (plus tard, je l'appellerai "registre A"). Dans STDCALL, il y a une différence, l'appelant ne vide pas la pile, l'appelant le fait.
Vous demandez lequel est le plus rapide. Personne. Vous devez utiliser la convention d'appel native aussi longtemps que vous le pouvez. Modifiez la convention uniquement s'il n'y a pas de solution, lors de l'utilisation de bibliothèques externes qui nécessitent l'utilisation d'une certaine convention.
En outre, il existe d'autres conventions que le compilateur peut choisir par défaut, c'est-à-dire que le compilateur Visual C ++ utilise FASTCALL qui est théoriquement plus rapide en raison d'une utilisation plus étendue des registres du processeur.
Habituellement, vous devez donner une signature de convention d'appel appropriée aux fonctions de rappel passées à une bibliothèque externe, c'est-à-dire que le rappel
qsort
de la bibliothèque C doit être CDECL (si le compilateur utilise par défaut une autre convention, nous devons marquer le rappel comme CDECL) ou divers rappels WinAPI doivent être STDCALL (tout WinAPI est STDCALL).Un autre cas habituel peut être celui où vous stockez des pointeurs vers certaines fonctions externes, c'est-à-dire que pour créer un pointeur vers la fonction WinAPI, sa définition de type doit être marquée avec STDCALL.
Et ci-dessous est un exemple montrant comment le compilateur le fait:
CDECL:
STDCALL:
la source
J'ai remarqué une publication qui dit que ce n'est pas grave si vous appelez
__stdcall
un__cdecl
ou vice versa. Cela fait.La raison: avec
__cdecl
les arguments qui sont passés aux fonctions appelées sont supprimés de la pile par la fonction appelante, dans__stdcall
, les arguments sont supprimés de la pile par la fonction appelée. Si vous appelez une__cdecl
fonction avec a__stdcall
, la pile n'est pas nettoyée du tout, donc éventuellement, lorsque le__cdecl
utilise une référence empilée pour les arguments ou l'adresse de retour, les anciennes données seront utilisées au pointeur de pile actuel. Si vous appelez une__stdcall
fonction à partir de a__cdecl
, la__stdcall
fonction nettoie les arguments sur la pile, puis la__cdecl
fonction le fait à nouveau, en supprimant éventuellement les informations de retour des fonctions appelantes.La convention Microsoft pour C essaie de contourner cela en modifiant les noms. Une
__cdecl
fonction est précédée d'un trait de soulignement. Une__stdcall
fonction préfixe avec un trait de soulignement et suffixe avec un signe arobase «@» et le nombre d'octets à supprimer. Par exemple,__cdecl
f (x) est lié comme_f
,__stdcall f(int x)
est lié comme_f@4
oùsizeof(int)
est 4 octets)Si vous parvenez à dépasser l'éditeur de liens, profitez du désordre de débogage.
la source
Je veux améliorer la réponse de @ adf88. Je pense que le pseudocode du STDCALL ne reflète pas la façon dont cela se passe dans la réalité. 'a', 'b' et 'c' ne sont pas extraits de la pile dans le corps de la fonction. Au lieu de cela, ils sont sautés par l'
ret
instruction (ret 12
qui serait utilisée dans ce cas) qui d'un seul coup retourne à l'appelant et en même temps fait apparaître «a», «b» et «c» de la pile.Voici ma version corrigée selon ma compréhension:
STDCALL:
la source
Il est spécifié dans le type de fonction. Lorsque vous avez un pointeur de fonction, il est supposé être cdecl sinon explicitement stdcall. Cela signifie que si vous obtenez un pointeur stdcall et un pointeur cdecl, vous ne pouvez pas les échanger. Les deux types de fonctions peuvent s'appeler sans problème, il s'agit simplement d'obtenir un type lorsque vous attendez l'autre. Quant à la vitesse, ils jouent tous les deux les mêmes rôles, juste dans un endroit très légèrement différent, c'est vraiment hors de propos.
la source
L'appelant et l'appelé doivent utiliser la même convention au moment de l'invocation - c'est la seule façon dont cela pourrait fonctionner de manière fiable. L'appelant et l'appelé suivent un protocole prédéfini - par exemple, qui doit nettoyer la pile. Si les conventions ne correspondent pas, votre programme se heurte à un comportement indéfini - il se bloque probablement de manière spectaculaire.
Ceci n'est requis que par site d'invocation - le code d'appel lui-même peut être une fonction avec n'importe quelle convention d'appel.
Vous ne devriez pas remarquer de réelle différence de performances entre ces conventions. Si cela devient un problème, vous devez généralement effectuer moins d'appels - par exemple, modifiez l'algorithme.
la source
Ces éléments sont spécifiques au compilateur et à la plate-forme. Ni le standard C ni le standard C ++ ne disent quoi que ce soit sur les conventions d'appel, sauf
extern "C"
en C ++.L'appelant connaît la convention d'appel de la fonction et gère l'appel en conséquence.
Oui.
Cela fait partie de la déclaration de fonction.
L'appelant connaît les conventions d'appel et peut agir en conséquence.
Non, la convention d'appel fait partie de la déclaration d'une fonction afin que le compilateur sache tout ce qu'il doit savoir.
Non. Pourquoi devrait-il?
Je ne sais pas. Essaye-le.
la source
Le
cdecl
modificateur fait partie du prototype de fonction (ou du type de pointeur de fonction, etc.) de sorte que l'appelant obtient les informations à partir de là et agit en conséquence.Non c'est bon.
En général, je m'abstiendrai de telles déclarations. La distinction compte par exemple. lorsque vous souhaitez utiliser les fonctions va_arg. En théorie, cela pourrait être
stdcall
plus rapide et générer du code plus petit car cela permet de combiner le popping les arguments avec le popping les locaux, mais avec OTOHcdecl
, vous pouvez faire la même chose, aussi, si vous êtes intelligent.Les conventions d'appel qui visent à être plus rapides font généralement un passage de registre.
la source
Les conventions d'appel n'ont rien à voir avec les langages de programmation C / C ++ et sont plutôt spécifiques sur la façon dont un compilateur implémente le langage donné. Si vous utilisez systématiquement le même compilateur, vous n'aurez jamais à vous soucier des conventions d'appel.
Cependant, nous voulons parfois que le code binaire compilé par différents compilateurs interagisse correctement. Lorsque nous le faisons, nous devons définir quelque chose appelé l'interface binaire d'application (ABI). L'ABI définit la manière dont le compilateur convertit la source C / C ++ en code machine. Cela inclura les conventions d'appel, la modification des noms et la disposition de la v-table. cdelc et stdcall sont deux conventions d'appel différentes couramment utilisées sur les plates-formes x86.
En plaçant les informations sur la convention d'appel dans l'en-tête source, le compilateur saura quel code doit être généré pour interagir correctement avec l'exécutable donné.
la source