In C ++ tun variadische Funktionen (die mit ... am Ende der Parameterliste) unbedingt folgen dem __cdecl Aufrufkonvention?

StackOverflow https://stackoverflow.com/questions/2512746

Frage

Ich weiß, dass __stdcall Funktionen nicht Ellipsen haben kann, aber ich will sicher sein, es gibt keine Plattformen, die die stdarg.h Funktionen für Aufrufkonventionen andere als __cdecl oder __stdcall unterstützen.

War es hilfreich?

Lösung

Die Aufrufkonvention muss man sein, wenn der Anrufer die Argumente vom Stapel löscht (weil die Angerufene nicht weiß, was übergeben wird).

Das entspricht nicht unbedingt dem, was Microsoft als „__cdecl“ though. Nur zum Beispiel auf einem SPARC, wird es normalerweise die Argumente in Registern übergeben, denn das ist, wie die SPARC Arbeit ausgelegt ist - seine Register grundsätzlich als Call-Stack handeln, die in den Hauptspeichern verschüttet werden, wenn die Anrufe tief genug, dass sie werden nicht mehr in dem Register passen.

Obwohl ich bin weniger sicher darüber, würde ich in etwa gleich auf IA64 (Itanium) erwarten - es hat auch einen großen Registersatz (ein paar hundert, wenn der Speicher dient). Wenn mich nicht alles täuscht, ist es ein bisschen mehr permissive darüber, wie Sie die Register verwenden, aber ich würde erwarten, dass es in ähnlicher Weise zumindest ein großer Teil der Zeit verwendet werden.

Warum ist das wichtig für Sie? Der Punkt, der mit stdarg.h und seine Makros zu verstecken Unterschiede in Konvention aus dem Code aufrufen, so dass es portabel mit variablen Argumenten arbeiten kann.

Bearbeiten, basierend auf Kommentare: Okay, jetzt verstehe ich, was Sie tun (zumindest genug, um die Antwort zu verbessern). Da Sie bereits (scheinbar) Code haben die Variationen in der Standard-ABI zu handhaben, sind die Dinge einfacher. Damit bleibt nur die Frage, ob variadische Funktionen immer das „default ABI“, was immer das für die Plattform zur Hand sein geschieht. Mit „stdcall“ und „default“ als die einzigen Optionen, ich denke, die Antwort auf diese Frage ist ja. Nur zum Beispiel auf Windows, wsprintf und wprintf die Daumenregel brechen und Verwendungen cdecl Konvention statt stdcall aufrufen.

Andere Tipps

Die endgültige Art und Weise, dass Sie dies feststellen können, ist es, die Aufrufkonventionen zu analysieren. Für variadische Funktionen zu arbeiten, braucht Ihre Aufrufkonvention ein paar Attribute:

  • Der Angerufene muss die Parameter zugreifen kann, die nicht Teil der variablen Argumentliste sind von einem von der Oberseite des Stapels festen Versatz. Dies erfordert, dass der Compiler die Parameter auf den Stapel von rechts nach links schieben. (Dazu gehören solche Dinge als ersten Parameter zu printf, die Formatspezifikation. Auch die Adresse der Variable Argumentliste selbst auch von einem bekannten Ort abgeleitet werden müssen.)
  • Der Anrufer muss zum Entfernen der Parameter vom Stapel verantwortlich sein, sobald die Funktion zurückgekehrt ist, weil nur der Compiler, während der Code für den Anrufer zu erzeugen, weiß, wie viele Parameter wurden auf den Stapel an erster Stelle geschoben. Die variadische Funktion selbst nicht über diese Informationen verfügen.

stdcall wird nicht funktionieren, weil die Angerufene für popping Parameter vom Stapel verantwortlich ist. In den alten 16-Bit-Windows-Tagen, pascal würde nicht funktionieren, weil sie Parameter auf den Stapel geschoben von links nach rechts.

Natürlich, wie die anderen Antworten auf hingewiesen haben, können Sie viele Plattformen nicht geben eine Wahl in Bezug auf die Aufrufkonvention, so dass diese Frage nicht relevant für die diejenigen.

Betrachten Sie die folgende Funktion auf einem x86-System:

void __stdcall etwas (char *, ...);

Die Funktion erklärt sich als __stdcall, die ein Angerufener-clean-Konvention ist. Aber eine variadische Funktion kann nicht sein Rufenen-sauber, da die Angerufene nicht weiß, wie viele Parameter übergeben wurden, so dass er nicht weiß, wie viele es sollte reinigen.

Die Microsoft Visual Studio C / C ++ Compiler löst diesen Konflikt durch leise die Aufrufkonvention zu __cdecl Umwandlung, die die einzigen unterstützte variadische Aufrufkonvention für Funktionen, die einen versteckten diesen Parameter nicht nehmen Sie.

Warum diese Umwandlung stattfindet still, anstatt eine Warnung oder einen Fehler zu erzeugen?

Meine Vermutung ist, dass es die Compiler-Optionen / Gr (set Standardaufrufkonvention __fastcall) zu machen und / Gz (set Standardkonvention __stdcall Aufruf) weniger ärgerlich.

Automatische Konvertierung von variadische Funktionen __cdecl bedeutet, dass Sie nur den / Gr hinzufügen oder / Gz Befehlszeilenoption zu Ihren Compiler-Optionen, und alles wird noch kompiliert und ausgeführt (nur mit der neuen Aufrufkonvention).

Eine andere Möglichkeit, dies zu betrachten ist nicht durch Denken des Compilers als Umwandlung variadische __stdcall zu __cdecl sondern durch ein einfaches „für variadische Funktionen, __stdcall ist Anrufer-clean.“

hier klicken

Haben Sie bedeuten ‚Plattformen von MSVC unterstützt‚oder als allgemeine Regel? Selbst wenn man sich auf die von MSVC unterstützten Plattformen beschränken, haben Sie immer noch Situationen wie IA64 und

AFAIK, ist die Vielfalt der Aufrufkonventionen einzigartig für DOS / Windows auf x86. Die meisten anderen Plattformen hatte Compiler mit dem Betriebssystem kommen und die Konvention standardisieren.

scroll top