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.
push arg1
push arg2
push arg3
call function
add sp,12
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.
push arg1
push arg2
push arg3
call function
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