Alle Funktionen in C / C ++ haben eine bestimmte Aufrufkonvention. Der Zweck einer Aufrufkonvention besteht darin, festzulegen, wie Daten zwischen dem Anrufer und dem Angerufenen übertragen werden und wer für Vorgänge wie das Bereinigen des Anrufstapels verantwortlich ist.
Die beliebtesten Anrufkonventionen unter Windows sind
__stdcall
, Schiebt die Parameter auf dem Stapel in umgekehrter Reihenfolge (von rechts nach links)
__cdecl
, Schiebt die Parameter auf dem Stapel in umgekehrter Reihenfolge (von rechts nach links)
__clrcall
Laden Sie die Parameter der Reihe nach (von links nach rechts) in den CLR-Ausdrucksstapel.
__fastcall
, In Registern gespeichert, dann auf Stapel geschoben
__thiscall
Auf Stapel geschoben; Dieser Zeiger ist in ECX gespeichert
Das Hinzufügen dieses Bezeichners zur Funktionsdeklaration teilt dem Compiler im Wesentlichen mit, dass diese bestimmte Funktion diese bestimmte Aufrufkonvention haben soll.
Die Aufrufkonventionen sind hier dokumentiert
Raymond Chen hat auch eine lange Reihe über die Geschichte der verschiedenen Aufrufkonventionen (5 Teile) gemacht, die hier beginnen.
Traditionell werden C-Funktionsaufrufe ausgeführt, bei denen der Aufrufer einige Parameter auf den Stapel schiebt, die Funktion aufruft und dann den Stapel öffnet, um diese übertragenen Argumente zu bereinigen.
/* example of __cdecl */ push arg1 push arg2 push arg3 call function add sp,12 // effectively "pop; pop; pop"
Hinweis: Die oben gezeigte Standardkonvention heißt __cdecl.
Die andere beliebteste Konvention ist __stdcall. Darin werden die Parameter erneut vom Aufrufer gepusht, aber der Stapel wird vom Angerufenen bereinigt. Dies ist die Standardkonvention für Win32-API-Funktionen (wie vom WINAPI-Makro in definiert) und wird manchmal auch als "Pascal" -Aufrufkonvention bezeichnet.
/* example of __stdcall */ push arg1 push arg2 push arg3 call function // no stack cleanup - callee does this
Dies sieht nach einem kleinen technischen Detail aus. Wenn jedoch Unstimmigkeiten darüber bestehen, wie der Stapel zwischen dem Anrufer und dem Angerufenen verwaltet wird, wird der Stapel auf eine Weise zerstört, die wahrscheinlich nicht wiederhergestellt werden kann. Da __stdcall die Stapelbereinigung durchführt, befindet sich der (sehr kleine) Code zum Ausführen dieser Aufgabe nur an einer Stelle und wird nicht wie in __cdecl in jedem Aufrufer dupliziert. Dadurch wird der Code geringfügig kleiner, obwohl die Auswirkungen auf die Größe nur in großen Programmen sichtbar sind.
Variadische Funktionen wie printf () können mit __stdcall kaum richtig ausgeführt werden, da nur der Aufrufer wirklich weiß, wie viele Argumente übergeben wurden, um sie zu bereinigen. Der Angerufene kann einige gute Vermutungen anstellen (z. B. durch Betrachten einer Formatzeichenfolge), aber die Stapelbereinigung müsste durch die tatsächliche Logik der Funktion bestimmt werden, nicht durch den Aufrufkonventionsmechanismus selbst. Daher unterstützt nur __cdecl verschiedene Funktionen, damit der Aufrufer die Bereinigung durchführen kann.
Dekorationen von Linkersymbolnamen: Wie oben in einem Aufzählungspunkt erwähnt, kann das Aufrufen einer Funktion mit der "falschen" Konvention katastrophal sein. Microsoft verfügt daher über einen Mechanismus, um dies zu verhindern. Es funktioniert gut, obwohl es verrückt sein kann, wenn man die Gründe nicht kennt. Sie haben beschlossen, dies zu beheben, indem sie die aufrufende Konvention in die Funktionsnamen auf niedriger Ebene mit zusätzlichen Zeichen (die häufig als "Dekorationen" bezeichnet werden) codieren. Diese werden vom Linker als nicht verwandte Namen behandelt. Die Standardaufrufkonvention ist __cdecl, aber jede kann explizit mit dem / G? Parameter an den Compiler.
__cdecl (cl / Gd ...)
Allen Funktionsnamen dieses Typs wird ein Unterstrich vorangestellt, und die Anzahl der Parameter spielt keine Rolle, da der Aufrufer für die Stapeleinrichtung und die Stapelbereinigung verantwortlich ist. Es ist möglich, dass ein Anrufer und ein Angeregter über die Anzahl der tatsächlich übergebenen Parameter verwirrt sind, aber zumindest die Stapeldisziplin wird ordnungsgemäß beibehalten.
__stdcall (cl / Gz ...)
Diesen Funktionsnamen wird ein Unterstrich vorangestellt und mit @ plus der Anzahl der übergebenen Bytes von Parametern versehen. Durch diesen Mechanismus ist es nicht möglich, eine Funktion mit dem "falschen" Typ oder sogar mit der falschen Anzahl von Parametern aufzurufen.
__fastcall (cl / Gr ...)
Diese Funktionsnamen beginnen mit einem @ -Zeichen und werden mit dem @ -Parameter-Zähler versehen, ähnlich wie __stdcall.
Beispiele:
Declaration -----------------------> decorated name void __cdecl foo(void); -----------------------> _foo void __cdecl foo(int a); -----------------------> _foo void __cdecl foo(int a, int b); -----------------------> _foo void __stdcall foo(void); -----------------------> _foo@0 void __stdcall foo(int a); -----------------------> _foo@4 void __stdcall foo(int a, int b); -----------------------> _foo@8 void __fastcall foo(void); -----------------------> @foo@0 void __fastcall foo(int a); -----------------------> @foo@4 void __fastcall foo(int a, int b); -----------------------> @foo@8
quelle
__stdcall ist eine aufrufende Konvention: Eine Methode zum Bestimmen, wie Parameter an eine Funktion (auf dem Stapel oder in Registern) übergeben werden und wer für die Bereinigung nach der Rückkehr der Funktion verantwortlich ist (der Aufrufer oder der Angerufene).
Raymond Chen hat einen Blog über die wichtigsten x86-Aufrufkonventionen geschrieben , und es gibt auch einen schönen CodeProject-Artikel .
Zum größten Teil sollten Sie sich keine Sorgen machen müssen. Der einzige Fall, in dem Sie dies tun sollten, ist, wenn Sie eine Bibliotheksfunktion aufrufen, die etwas anderes als die Standardfunktion verwendet. Andernfalls generiert der Compiler den falschen Code und Ihr Programm stürzt wahrscheinlich ab.
quelle
Leider gibt es keine einfache Antwort, wann und wann nicht.
__stdcall bedeutet, dass die Argumente für eine Funktion vom ersten bis zum letzten auf den Stapel verschoben werden. Dies steht im Gegensatz zu __cdecl, was bedeutet, dass die Argumente von last nach first verschoben werden, und __fastcall, bei dem die ersten vier (glaube ich) Argumente in Registern abgelegt werden und der Rest auf dem Stapel abgelegt wird.
Sie müssen nur wissen, was der Angerufene erwartet, oder wenn Sie eine Bibliothek schreiben, was Ihre Anrufer wahrscheinlich erwarten, und sicherstellen, dass Sie die von Ihnen gewählte Konvention dokumentieren.
quelle
__stdcall
und__cdecl
unterscheiden sich nur in der Verantwortung für die Reinigung nach der Rückgabe (und der Dekoration). Die Argumentübergabe ist für beide gleich (von rechts nach links). Was Sie beschreiben, ist die Pascal-Aufrufkonvention.Es gibt eine Aufrufkonvention für eine Funktion an. Eine aufrufende Konvention ist eine Reihe von Regeln, wie Parameter an eine Funktion übergeben werden: in welcher Reihenfolge, pro Adresse oder pro Kopie, wer die Parameter bereinigen soll (Anrufer oder Angerufene) usw.
quelle
__stdcall bezeichnet eine aufrufende Konvention ( einige Details finden Sie in diesem PDF ). Dies bedeutet, dass angegeben wird, wie Funktionsargumente vom Stapel verschoben und abgelegt werden und wer dafür verantwortlich ist.
__stdcall ist nur eine von mehreren Aufrufkonventionen und wird in der gesamten WINAPI verwendet. Sie müssen es verwenden, wenn Sie Funktionszeiger als Rückruf für einige dieser Funktionen bereitstellen. Im Allgemeinen müssen Sie in Ihrem Code keine bestimmte Aufrufkonvention angeben, sondern nur die Standardeinstellung des Compilers verwenden, mit Ausnahme des oben genannten Falls (Rückrufe auf Code von Drittanbietern).
quelle
Dies ist eine Aufrufkonvention, nach der WinAPI-Funktionen ordnungsgemäß aufgerufen werden müssen. Eine aufrufende Konvention besteht aus einer Reihe von Regeln, wie die Parameter an die Funktion übergeben werden und wie der Rückgabewert von der Funktion übergeben wird.
Wenn der Aufrufer und der aufgerufene Code unterschiedliche Konventionen verwenden, stoßen Sie auf undefiniertes Verhalten (wie einen so seltsam aussehenden Absturz ).
C ++ - Compiler verwenden standardmäßig nicht __stdcall - sie verwenden andere Konventionen. Um WinAPI-Funktionen von C ++ aus aufzurufen, müssen Sie angeben, dass sie __stdcall verwenden. Dies erfolgt normalerweise in Windoes SDK-Headerdateien und auch beim Deklarieren von Funktionszeigern.
quelle
Einfach ausgedrückt, wenn Sie die Funktion aufrufen, wird sie in den Stapel / das Register geladen. __stdcall ist eine Konvention / ein Weg (zuerst das rechte Argument, dann das linke Argument ...). __decl ist eine andere Konvention, mit der die Funktion auf den Stapel oder die Register geladen wird.
Wenn Sie sie verwenden, weisen Sie den Computer an, diese Funktion zum Laden / Entladen der Funktion während der Verknüpfung zu verwenden, sodass Sie keine Nichtübereinstimmung / keinen Absturz erhalten.
Andernfalls verwenden der Funktionsanrufer und der Funktionsaufruf möglicherweise unterschiedliche Konventionen, die zum Absturz des Programms führen.
quelle
__stdcall ist die aufrufende Konvention, die für die Funktion verwendet wird. Dies teilt dem Compiler die Regeln mit, die für das Einrichten des Stapels, das Übertragen von Argumenten und das Abrufen eines Rückgabewerts gelten. Es gibt eine Reihe anderer Aufrufkonventionen wie __cdecl , __thiscall , __fastcall und __naked .
__stdcall ist die Standardaufrufkonvention für Win32-Systemaufrufe.
Weitere Details finden Sie auf Wikipedia .
quelle