Was sind die verschiedenen Aufrufkonventionen in C/C++ und was bedeuten sie jeweils?

Lesezeit: 10 Minuten

Was sind die verschiedenen Aufrufkonventionen in CC und was bedeuten
Prabhu R

In C/C++ sind verschiedene Aufrufkonventionen verfügbar: stdcall, extern, pascalusw. Wie viele solcher Aufrufkonventionen sind verfügbar, und was bedeuten sie jeweils? Gibt es Links, die diese beschreiben?

Einfache Antwort: Ich verwende cdecl, stdcall und fastcall. Fastcall nutze ich selten. stdcall wird zum Aufrufen von Windows-API-Funktionen verwendet.

Ausführliche Antwort (Gestohlen von Wikipedia):

cdekl – In cdecl werden Subroutinenargumente auf dem Stack übergeben. Ganzzahlige Werte und Speicheradressen werden im EAX-Register zurückgegeben, Fließkommawerte im ST0 x87-Register. Die Register EAX, ECX und EDX werden vom Aufrufer gespeichert, und der Rest wird vom Angerufenen gespeichert. Die x87-Gleitkommaregister ST0 bis ST7 müssen beim Aufrufen einer neuen Funktion leer sein (gepoppt oder freigegeben), und ST1 bis ST7 müssen beim Verlassen einer Funktion leer sein. ST0 muss auch leer sein, wenn es nicht zur Rückgabe eines Werts verwendet wird.

Systemaufruf – Dies ähnelt cdecl darin, dass Argumente von rechts nach links geschoben werden. EAX, ECX und EDX werden nicht beibehalten. In AL wird die Größe der Parameterliste in Doppelworten übergeben.

paskal – Die Parameter werden von links nach rechts (im Gegensatz zu cdecl) auf den Stack geschoben, und der Aufgerufene ist für den Ausgleich des Stacks vor der Rückgabe verantwortlich.

Standardanruf – Der Standardruf[4] Die Aufrufkonvention ist eine Variation der Pascal-Aufrufkonvention, bei der der Aufgerufene für das Bereinigen des Stacks verantwortlich ist, die Parameter jedoch wie in der _cdecl-Aufrufkonvention von rechts nach links auf den Stack geschoben werden. Die Register EAX, ECX und EDX sind für die Verwendung innerhalb der Funktion vorgesehen. Rückgabewerte werden im EAX-Register gespeichert.

Schnellruf – Die __fastcall-Konvention (auch bekannt als __msfastcall) übergibt die ersten beiden Argumente (von links nach rechts ausgewertet), die in ECX und EDX passen. Verbleibende Argumente werden von rechts nach links auf den Stack geschoben.

Vektoraufruf – In Visual Studio 2013 führte Microsoft die __vectorcall-Aufrufkonvention als Reaktion auf Effizienzbedenken von Spiele-, Grafik-, Video-/Audio- und Codec-Entwicklern ein.[7] Für IA-32- und x64-Code ähnelt __vectorcall __fastcall bzw. den ursprünglichen x64-Aufrufkonventionen, erweitert sie jedoch, um die Übergabe von Vektorargumenten mithilfe von SIMD-Registern zu unterstützen. Wenn eines der ersten sechs Argumente für x64 Vektortypen ist (float, double, __m128, __m256 usw.), werden sie über die entsprechenden XMM/YMM-Register übergeben. In ähnlicher Weise werden für IA-32 bis zu sechs XMM/YMM-Register unabhängig von der Position sequentiell für Vektorargumente von links nach rechts zugewiesen. Darüber hinaus fügt __vectorcall Unterstützung für die Übergabe von HVA-Werten (Homogen Vector Aggregate) hinzu, bei denen es sich um zusammengesetzte Typen handelt, die ausschließlich aus bis zu vier identischen Vektortypen bestehen und dieselben sechs Register verwenden. Sobald die Register für Argumente vom Vektortyp zugewiesen wurden, werden die unbenutzten Register unabhängig von der Position von links nach rechts HVA-Argumenten zugewiesen. Die resultierenden Vektortyp- und HVA-Werte werden unter Verwendung der ersten vier XMM/YMM-Register zurückgegeben.

SafeCall – In Delphi und Free Pascal unter Microsoft Windows kapselt die Safecall-Aufrufkonvention die COM-Fehlerbehandlung (Component Object Model), sodass Ausnahmen nicht an den Aufrufer weitergegeben werden, sondern im HRESULT-Rückgabewert gemeldet werden, wie von COM/OLE gefordert . Beim Aufruf einer Safecall-Funktion aus Delphi-Code überprüft Delphi automatisch auch das zurückgegebene HRESULT und löst bei Bedarf eine Ausnahme aus.

Die Safecall-Aufrufkonvention ist die gleiche wie die stdcall-Aufrufkonvention, außer dass Ausnahmen in EAX als HResult an den Aufrufer zurückgegeben werden (anstatt in FS:[0]), während das Funktionsergebnis als Referenz auf dem Stapel übergeben wird, als wäre es ein abschließender „out“-Parameter. Beim Aufrufen einer Delphi-Funktion aus Delphi erscheint diese Aufrufkonvention genau wie jede andere Aufrufkonvention, denn obwohl Ausnahmen in EAX zurückgegeben werden, werden sie vom Aufrufer automatisch wieder in richtige Ausnahmen umgewandelt. Bei der Verwendung von COM-Objekten, die in anderen Sprachen erstellt wurden, werden die HResults automatisch als Ausnahmen ausgelöst, und das Ergebnis für Get-Funktionen befindet sich im Ergebnis und nicht in einem Parameter. Beim Erstellen von COM-Objekten in Delphi mit Safecall müssen Sie sich keine Gedanken über HResults machen, da Ausnahmen wie gewohnt ausgelöst werden können, in anderen Sprachen jedoch als HResults angezeigt werden.

Microsoft X64-Anrufkonvention – Die Microsoft x64-Anrufkonvention[12][13] wird unter Windows gefolgt und UEFI vor dem Booten (für den langen Modus auf x86-64). Es verwendet die Register RCX, RDX, R8, R9 für die ersten vier Ganzzahl- oder Zeigerargumente (in dieser Reihenfolge), und XMM0, XMM1, XMM2, XMM3 werden für Gleitkommaargumente verwendet. Zusätzliche Argumente werden auf den Stapel geschoben (von rechts nach links). Ganzzahlige Rückgabewerte (ähnlich x86) werden in RAX zurückgegeben, wenn 64 Bit oder weniger vorhanden sind. Fließkomma-Rückgabewerte werden in XMM0 zurückgegeben. Parameter mit einer Länge von weniger als 64 Bit werden nicht durch Nullen erweitert; die hohen Bits werden nicht auf Null gesetzt.

Beim Kompilieren für die x64-Architektur in einem Windows-Kontext (ob mit Microsoft- oder Nicht-Microsoft-Tools) gibt es nur eine Aufrufkonvention – die hier beschriebene, sodass stdcall, thiscall, cdecl, fastcall usw. jetzt alle eins sind und das gleiche.

In der Microsoft x64-Aufrufkonvention liegt es in der Verantwortung des Aufrufers, unmittelbar vor dem Aufruf der Funktion 32 Byte “Schattenspeicher” auf dem Stapel zuzuweisen (unabhängig von der tatsächlichen Anzahl der verwendeten Parameter) und den Stapel nach dem Aufruf zu öffnen. Der Schattenraum wird verwendet, um RCX, RDX, R8 und R9 zu verschütten,[14] sondern muss allen Funktionen zur Verfügung gestellt werden, auch solchen mit weniger als vier Parametern.

Die Register RAX, RCX, RDX, R8, R9, R10, R11 gelten als flüchtig (vom Anrufer gespeichert).[15]

Die Register RBX, RBP, RDI, RSI, RSP, R12, R13, R14 und R15 werden als nichtflüchtig (aufgerufen gespeichert) betrachtet.[15]

Zum Beispiel nimmt eine Funktion, die 5 ganzzahlige Argumente verwendet, das erste bis vierte in den Registern, und das fünfte wird an die Spitze des Schattenraums geschoben. Wenn also die aufgerufene Funktion eingegeben wird, besteht der Stapel (in aufsteigender Reihenfolge) aus der Rücksprungadresse, gefolgt vom Schattenraum (32 Bytes), gefolgt vom fünften Parameter.

In x86-64 speichert Visual Studio 2008 Gleitkommazahlen in XMM6 und XMM7 (sowie XMM8 bis XMM15); folglich müssen für x86-64 benutzerdefinierte Routinen in Assemblersprache XMM6 und XMM7 beibehalten (im Vergleich zu x86, wo benutzerdefinierte Routinen in Assemblersprache XMM6 und XMM7 nicht beibehalten mussten). Mit anderen Worten, vom Benutzer geschriebene Assembler-Routinen müssen aktualisiert werden, um XMM6 und XMM7 vor/nach der Funktion zu speichern/wiederherstellen, wenn sie von x86 auf x86-64 portiert werden.

  • Als Nebenbemerkung: Alle oben aufgeführten Punkte gelten für extern verknüpfte Funktionen und Funktionen, die extern aufgerufen werden können (ein Zeiger auf eine Funktion wird irgendwo in der Einheit verwendet). Wenn ein Compiler (ich spreche von x86 MSVC, aber es kann auf andere erweitert werden) daraus schließen kann, dass die Funktion nur intern verknüpft ist (innerhalb einer einzelnen Kompilierungseinheit – o/obj) UND es auch als weniger Overhead betrachtet wird, indem es nicht inliniert wird Erstens ist es frei, wie es Argumenten Register / Stack-Slots zuweist.

    – Arti

    5. Dezember 2018 um 19:04 Uhr

Weder Standard C noch Standard C++ haben ein solches Konzept – dies sind Funktionen bestimmter Compiler, Linker und/oder Betriebssysteme, daher sollten Sie wirklich angeben, an welchen spezifischen Technologien Sie interessiert sind.

  • In Anbetracht der Tags denke ich, dass er sich auf Microsoft Visual C++ bezog

    – Cătălin Pitiş

    4. Juni 2009 um 11:19 Uhr

  • Bitte überprüfen Sie den Bearbeitungsverlauf, bevor Sie solche Kommentare abgeben.

    anon

    4. Juni 2009 um 11:20 Uhr

  • Oh, deshalb habe ich nicht verstanden, warum die C++-Syntax plötzlich erlaubte, diesen zusätzlichen Typ zwischen den Rückgabetyp und den Funktionsnamen zu schieben? Es ist eigentlich kein Teil der gewöhnlichen C++-Syntax?

    – Kyle Delaney

    24. Juni 2017 um 16:02 Uhr

  • Obwohl K&R C eine hatte fortran dafür reserviertes Schlüsselwort, das kein Compiler jemals verwendet hat.

    – Davislor

    4. Juli 2017 um 2:35 Uhr

Standard-C++ hat grundsätzlich zwei: extern "C" und extern "C++". Letzteres ist die Standardeinstellung; Ersteres wird verwendet, wenn Sie mit C-Code verknüpfen müssen. Compiler können neben “C” und “C++” auch andere Zeichenfolgen definieren. Beispielsweise kann ein Compiler, der mit seinem Pascal-Geschwister kompatibel ist, definieren extern "Pascal".

Leider haben einige Compiler stattdessen Schlüsselwörter erfunden. Lesen Sie in diesen Fällen die Compiler-Dokumentation.

  • Keines davon sind Aufrufkonventionen – es sind Verknüpfungsspezifikationen.

    anon

    4. Juni 2009 um 11:09 Uhr

  • Nun, wie Sie selbst erwähnt haben, hat ISO C++ keine Vorstellung von “Aufrufkonventionen” und beschreibt auch nicht genau, was Verknüpfungsspezifikationen sind – nur was Sie damit machen können. Es ist also im Grunde eine Grauzone. Deshalb habe ich gegeben extern "Pascal" als Beispiel. Ein Compiler kann sicherlich ein anderes Registerzuweisungsschema für Funktionen mit dieser Verknüpfungsspezifikation verwenden.

    – MSalter

    4. Juni 2009 um 11:31 Uhr

  • Nein, tut mir leid, Sie führen hier eine MSVC-zentrierte Ansicht ein. ISO sagt definitiv NICHT, dass Linkage-Spezifikation (nur) Benennung ist. Und einer der Gründe, warum qsort() bei der Verknüpfung überladen wird, liegt darin, dass sich auf Nicht-MSVC-Plattformen die Stack-Anordnungen (“Aufrufkonventionen”) zwischen extern “C” und extern “C++” unterscheiden.

    – MSalter

    4. Juni 2009 um 12:37 Uhr

  • Keine Ahnung, warum dies abgelehnt wurde – die Verknüpfungsspezifikation ist sicherlich das einzige, was in Standard-C++ die Aufrufkonventionen ändern kann.

    – Johannes Schaub – litb

    4. Juni 2009 um 13:57 Uhr

  • @anon extern “Pascal” und extern “C” sind beide Verknüpfungsspezifikationen UND Aufrufkonventionsspezifikationen. Oder woher soll der Compiler sonst wissen, dass die erste Funktion mit Pascal-Aufrufkonventionen aufgerufen werden soll? Pascal schiebt normalerweise die Argumente von rechts nach links auf den Stack und dann die Rücksprungadresse. Der Aufgerufene räumt den Stack bei der Rückkehr auf. C macht normalerweise genau das Gegenteil (aufgrund möglicher Argumentlisten mit variabler Länge), schiebt von links nach rechts und der AUFRUFER löscht den Stapel, da nur er weiß, wie viele Argumente er bereitgestellt hat.

    – Angel O’Sphere

    3. September 2013 um 17:27 Uhr

Diese betreffen die Reihenfolge, in der Parameter in den Aufrufstapel gestellt werden, und wann Call-by-Value- und/oder Call-by-Reference-Semantiken verwendet werden sollen. Sie sind Compiler-spezifische Erweiterungen, die die mehrsprachige Programmierung vereinfachen sollen.

Was sind die verschiedenen Aufrufkonventionen in CC und was bedeuten
Davislor

Sie sind plattformspezifische Erweiterungen, die zum Aufrufen von Funktionen in bestimmten Bibliotheken, insbesondere der Win32-API, benötigt werden. Sie sind nicht standardisiert und für jeden Compiler spezifisch, obwohl die Optionen von MSVC die sind de facto Standard für Windows auf x86. Normalerweise deklariert eine Bibliothek, die sie benötigt, sie in den Header-Dateien und sie funktionieren transparent. Der Hauptunterschied zwischen ihnen besteht darin, dass C in der Vergangenheit eine weniger effiziente Konvention verwendet hat, die eine variable Anzahl von Argumenten beliebigen Typs zuließ, während Windows und die meisten anderen Sprachen dies anders machten. Viele der Unterschiede, wie z. B. linkshändiges oder rechtshändiges Drücken und Aufräumen durch den Aufrufer oder die aufgerufene Funktion, waren jedoch ziemlich willkürlich.

Sie sind für 64-Bit-Code weitgehend irrelevant: Die heiligen Kriege um Aufrufkonventionen haben auf diesen Plattformen nie stattgefunden.

Es gibt einige häufige Fälle, in denen Sie möglicherweise eine davon zu einer Funktion hinzufügen müssen. Ein C++-Modul, das mit in anderen Sprachen geschriebenen Modulen (und manchmal sogar mit anderen C++-Compilern) verknüpft werden muss, muss die verwenden extern "C" Namenskonvention für Kompatibilität. Eine Rückruffunktion muss dieselbe Aufrufkonvention verwenden wie der Aufrufer, der mit der Windows-API verwendet wird CALLBACK, nicht die Standardeinstellung. Eine gemeinsam genutzte Bibliothek muss möglicherweise ihre Funktionen mit einer anderen Aufrufkonvention exportieren, als sie intern verwendet, oder möchte möglicherweise davon Gebrauch machen __cdecl explizit, falls sich die Standardeinstellung ändert. Möglicherweise erhalten Sie eine bessere Leistung oder nicht __fastcall auf einigen Plattformen: Es beschleunigt meistens kurze Blattfunktionen mit einem oder zwei Parametern und könnte einige Programme langsamer machen.

1646266818 412 Was sind die verschiedenen Aufrufkonventionen in CC und was bedeuten
Alraune

fastcall ist optimiert, aber niemand verwendet es

917660cookie-checkWas sind die verschiedenen Aufrufkonventionen in C/C++ und was bedeuten sie jeweils?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy