Übergabe zu vieler Argumente an printf

Lesezeit: 6 Minuten

Jeder C-Programmierer, der länger als eine Woche gearbeitet hat, ist auf Abstürze gestoßen, die auf Aufrufe zurückzuführen sind printf mit mehr Formatbezeichnern als eigentlichen Argumenten, zB:

printf("Gonna %s and %s, %s!", "crash", "burn");

Gibt es jedoch ähnliche schlimme Dinge, die passieren können, wenn Sie bestehen? zu viele Argumente für printf?

printf("Gonna %s and %s!", "crash", "burn", "dude");

Mein Wissen über die x86/x64-Assemblierung lässt mich glauben, dass dies harmlos ist, obwohl ich nicht überzeugt bin, dass es keine Randbedingung gibt, die ich vermisse, und ich habe keine Ahnung von anderen Architekturen. Ist dieser Zustand garantiert harmlos, oder gibt es auch hier eine potentiell crashauslösende Falle?

  • Keine Antwort auf deine Frage, Stapler ist richtig, aber für die Abstürze. gcc sollte davor gut warnen, also gibt es wirklich keine Entschuldigung dafür, das zu übersehen 😉

    – Jens Gustedt

    26. August 2010 um 20:09 Uhr

  • Wie konnte GCC davor warnen? Beachten Sie, dass der Formatstring nicht unbedingt ein konstanter String sein muss. Es kann jede sein char *

    – Nathan Fellmann

    26. August 2010 um 20:24 Uhr

  • GCC kann gute Warnungen ausgeben, wenn es die Formatzeichenfolge zur Kompilierzeit kennen kann. Da dies einen großen Teil der rationalen Anwendungsfälle für darstellt printf und Freunde, diese Warnungen sind wertvoll und sollten beachtet werden.

    – RBerteig

    26. August 2010 um 21:00 Uhr

  • gcc kann auch Warnungen ausgeben, wenn der Format-String kein String-Literal ist, wenn ich mich richtig erinnere. Dies ist, um dumme Dinge wie zu fangen printf(mystring);.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    26. August 2010 um 23:43 Uhr

Online-C-Standardentwurf (n1256)Abschnitt 7.19.6.1, Absatz 2:

Die Funktion fprintf schreibt die Ausgabe in den Stream, auf den stream zeigt, unter der Kontrolle der Zeichenfolge, auf die format zeigt, die angibt, wie nachfolgende Argumente für die Ausgabe konvertiert werden. Wenn für das Format nicht genügend Argumente vorhanden sind, ist das Verhalten undefiniert. Wenn das Format erschöpft ist, während noch Argumente vorhanden sind, werden die überschüssigen Argumente (wie immer) ausgewertet, ansonsten aber ignoriert. Die Funktion fprintf gibt zurück, wenn das Ende der Formatzeichenfolge erreicht wird.

Verhalten für alle anderen *printf() Funktionen ist das gleiche bezüglich überschüssiger Argumente außer für vprintf() (offensichtlich).

  • Gilt dies für vom Benutzer geschriebene variadische Funktionen?

    – Benutzer253751

    17. März 2016 um 2:33 Uhr

  • @immibis: Der Hauptgrund, warum dies möglicherweise nicht der Fall ist, besteht darin, dass bei einigen Aufrufkonventionen die Pop-Argumente des Angerufenen bei der Rückkehr vom Stapel genommen werden. Diese Regel erfordert im Wesentlichen, dass die Implementierung den allgemeinen Fall unterstützt oder viel Unterstützung für spezielle Zwecke hat printf. Die meisten Systeme mit einer Callee-Pops-Aufrufkonvention verwenden sie aus diesem Grund nicht für verschiedene Funktionen. (Und auch die x86er ret imm16 Anweisung erfordert, dass die Anzahl der auszulesenden Bytes eine Kompilierzeitkonstante ist). printf ist so ziemlich der schlimmste Fall für die Fähigkeit des Angerufenen, die Anzahl der Argumente zu erkennen: polymorph, kein Wächter

    – Peter Cordes

    25. Mai 2016 um 17:45 Uhr

Benutzeravatar von torak
Torak

Sie kennen wahrscheinlich den Prototyp für die printf-Funktion als so etwas

int printf(const char *format, ...);

Eine vollständigere Version davon wäre eigentlich

int __cdecl printf(const char *format, ...);

Das __cdecl definiert die “Aufrufkonvention”, die neben anderen Dingen beschreibt, wie Argumente behandelt werden. In diesem Fall bedeutet dies, dass Argumente auf den Stack geschoben werden und dass der Stack von der aufrufenden Funktion bereinigt wird.

Eine Alternative zu _cdecl ist __stdcall, Da sind andere. Mit __stdcall Die Konvention ist, dass Argumente auf den Stack geschoben und von der aufgerufenen Funktion bereinigt werden. Allerdings ist das meines Wissens nach nicht möglich __stdcall Funktion, um eine variable Anzahl von Argumenten zu akzeptieren. Das macht Sinn, da es nicht wissen würde, wie viel Stack zu reinigen ist.

Das lange und das kurze davon ist das im Fall von __cdecl Es ist sicher, so viele Argumente zu übergeben, wie Sie möchten, da die Bereinigung im Code durchgeführt wird, der den Aufruf durchführt. Wenn Sie irgendwie zu viele Argumente an a übergeben würden __stdcall funktionieren, führt dies zu einer Beschädigung des Stacks. Ein Beispiel dafür, wo dies passieren könnte, ist, wenn Sie den falschen Prototyp hatten.

Weitere Informationen zu Anrufkonventionen finden Sie auf Wikipedia hier.

  • __cdecl ist ein Win32ismus, der durch die Tatsache entstanden ist, dass einige alte DOS-Compiler sowohl C- als auch Pascal-Aufrufkonventionen unterstützten.

    – ninjalj

    26. August 2010 um 21:46 Uhr

  • @ninjalj, __cdecl wird nur von MS-Compilern unterstützt, aber der allgemeine Hinweis zu Aufrufkonventionen gilt für alle Betriebssysteme.

    – JSBձոգչ

    26. August 2010 um 21:48 Uhr

  • @JSBangs: __cdecl wurde auch von den Borland-Compilern IIRC unterstützt. Außerdem verwendet C auf den meisten anderen Betriebssystemen nur die C-Aufrufkonvention (von rechts nach links, Aufrufer bereinigt den Stapel), möglicherweise mit Varianten für ISRs, Kompatibilität mit anderen Compilern und/oder Speichern der Argumente in Registern (regparm von GCC). AFAIK, Win32 ist die einzige Plattform, auf der Sie eine Aufrufkonvention auswählen können, die vararg-Funktionen nicht unterstützt.

    – ninjalj

    26. August 2010 um 21:56 Uhr

  • @ninjalj: Das 68000-basierte Macintosh-Betriebssystem verwendete für fast alles die “Pascal” -Aufrufkonvention (genannt Function Pops Stack). Eigentlich ein wenig ironisch, da diese Aufrufkonvention auf dem 68000 eine Sequenz wie “mov.l (A7+),A0 / addq #4,A7 / jmp (A0)” erfordern würde, während die C-Aufrufkonvention die Verwendung von erlauben würde die “RETURN”-Anweisung.

    – Superkatze

    26. August 2010 um 22:05 Uhr

  • -1 für die Darstellung von MS-isms, als ob sie Teil der C-Sprache wären.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    26. August 2010 um 23:46 Uhr

Benutzeravatar von Stapler
Stapler

Alle Argumente werden auf den Stapel geschoben und entfernt, wenn der Stapelrahmen entfernt wird. dieses Verhalten ist unabhängig von einem bestimmten Prozessor. (Ich erinnere mich nur an einen Mainframe, der keinen Stack hatte und in den 70er Jahren entworfen wurde.) Also, ja, das zweite Beispiel wird nicht scheitern.

printf ist so konzipiert, dass es eine beliebige Anzahl von Argumenten akzeptiert. printf liest dann den Formatbezeichner (erstes Argument) und zieht nach Bedarf Argumente aus der Argumentliste. Aus diesem Grund stürzen zu wenige Argumente ab: Der Code beginnt einfach, nicht vorhandene Argumente zu verwenden, auf Speicher zuzugreifen, der nicht vorhanden ist, oder etwas anderes Schlechtes. Aber bei zu vielen Argumenten werden die zusätzlichen Argumente einfach ignoriert. Der Formatbezeichner verwendet weniger Argumente als übergeben wurden.

Kommentar: Sowohl gcc als auch clang erzeugen Warnungen:

$ clang main.c 
main.c:4:29: warning: more '%' conversions than data arguments [-Wformat]
  printf("Gonna %s and %s, %s!", "crash", "burn");
                           ~^
main.c:5:47: warning: data argument not used by format string 
                      [-Wformat-extra-args]
  printf("Gonna %s and %s!", "crash", "burn", "dude");
         ~~~~~~~~~~~~~~~~~~                   ^
2 warnings generated.

  • Das Problem tritt hauptsächlich auf, wenn Sie Ihre eigene Formatzeichenfolge generieren, die zur Kompilierzeit nicht bekannt ist.

    – meneldal

    22. November 2016 um 5:46 Uhr

  • Das Problem tritt hauptsächlich auf, wenn Sie Ihre eigene Formatzeichenfolge generieren, die zur Kompilierzeit nicht bekannt ist.

    – meneldal

    22. November 2016 um 5:46 Uhr

1398810cookie-checkÜbergabe zu vieler Argumente an printf

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

Privacy policy