Wozu dient der Formatbezeichner %n in C?

Lesezeit: 10 Minuten

Benutzeravatar von Josh
Josch

Was nützt die %n Formatbezeichner in C? Könnte jemand mit einem Beispiel erklären?

  • Was ist aus der hohen Kunst geworden, das feine Handbuch zu lesen?

    – Jens

    21. Juni 2014 um 11:41 Uhr

  • Ich denke, die eigentliche Frage ist, was das ist PUNKT der eine Option wie diese? warum sollte jemand den Wert der Anzahl der gedruckten Zeichen wissen wollen, geschweige denn, diesen Wert direkt in den Speicher schreiben. Es war, als wären die Entwickler gelangweilt und hätten beschlossen, einen Fehler in den Kernal einzuführen

    – Jia Chen

    9. Februar 2018 um 1:31 Uhr

  • Deshalb hat Bionic es gelassen.

    – solidak

    4. September 2018 um 15:24 Uhr

  • Es ist in der Tat eine berechtigte Frage, die die guten Handbücher wahrscheinlich nicht beantworten werden; das wurde festgestellt %n macht printf versehentlich Turing-komplett und man kann zB Brainfuck darin einbauen, siehe github.com/HexHive/printbf und oilhell.org/blog/2019/02/07.html#appendix-a-minor-sublanguages

    – John Frazer

    26. April 2020 um 9:36 Uhr


Benutzeravatar von jamesdlin
jamesdlin

Die meisten dieser Antworten erklären was %n tut (was bedeutet, nichts zu drucken und die Anzahl der bisher gedruckten Zeichen in eine zu schreiben int Variable), aber bisher hat niemand wirklich ein Beispiel dafür gegeben, was verwenden es hat. Hier ist eine:

int n;
printf("%s: %nFoo\n", "hello", &n);
printf("%*sBar\n", n, "");

wird drucken:

hello: Foo
       Bar

mit Foo und Bar ausgerichtet. (Es ist trivial, das zu tun, ohne zu verwenden %n für dieses spezielle Beispiel, und im Allgemeinen könnte man das immer zuerst auflösen printf Anruf:

int n = printf("%s: ", "hello");
printf("Foo\n");
printf("%*sBar\n", n, "");

Ob es sich lohnt, etwas Esoterisches zu verwenden, wenn die etwas zusätzliche Bequemlichkeit es wert ist %n (und möglicherweise Fehler einzuführen) ist offen für Diskussionen.)

  • Oh mein Gott – dies ist eine zeichenbasierte Version der Berechnung der Pixelgröße einer Zeichenfolge in einer bestimmten Schriftart!

    Benutzer3458

    4. August 2010 um 12:41 Uhr

  • Können Sie erklären, warum &n und *s benötigt werden? Sind das beide Anhaltspunkte?

    – Andreas S

    17. Januar 2014 um 1:05 Uhr

  • @AndrewS &n ist ein Zeiger (& ist die Adresse des Betreibers); ein Zeiger ist notwendig, weil C eine Wertübergabe ist, und ohne Zeiger, printf konnte den Wert von nicht ändern n. Das %*s Verwendung in der printf format string druckt a %s Bezeichner (in diesem Fall die leere Zeichenfolge "") Verwendung einer Feldbreite von n Figuren. Eine grundlegende Erklärung printf Grundsätze liegt im Grunde außerhalb des Rahmens dieser Frage (und Antwort); Ich würde empfehlen, die zu lesen printf Dokumentation oder stellen Sie Ihre eigene separate Frage zu SO.

    – jamesdlin

    17. Januar 2014 um 8:59 Uhr

  • Vielen Dank für das Zeigen eines Anwendungsfalls. Ich verstehe nicht, warum Leute das Handbuch einfach kopieren und in SO einfügen und es manchmal umformulieren. Wir sind Menschen und alles wird für a getan Grund was immer in einer Antwort erklärt werden sollte. “Tut nichts” ist wie zu sagen “Das Wort cool bedeutet cool” – fast nutzloses Wissen.

    – the_endian

    25. Januar 2017 um 20:59 Uhr

  • @PSkocik Es ist kompliziert und fehleranfällig genug, ohne eine zusätzliche Ebene der Indirektion hinzuzufügen.

    – jamesdlin

    24. Juni 2019 um 15:22 Uhr

Benutzeravatar von Starkey
Stern-Taste

Nichts gedruckt. Das Argument muss ein Zeiger auf ein signed int sein, wo die Anzahl der bisher geschriebenen Zeichen gespeichert wird.

#include <stdio.h>

int main()
{
  int val;

  printf("blah %n blah\n", &val);

  printf("val = %d\n", val);

  return 0;

}

Der vorherige Code druckt:

blah  blah
val = 5

  • Sie erwähnen, dass das Argument ein Zeiger auf ein signed int sein muss, dann haben Sie in Ihrem Beispiel ein unsigned int verwendet (wahrscheinlich nur ein Tippfehler).

    – bta

    3. August 2010 um 22:21 Uhr


  • @AndrewS: Weil die Funktion den Wert der Variablen ändert.

    – Jack

    20. Juni 2014 um 0:31 Uhr

  • @Jack int ist immer unterschrieben.

    – jamesdlin

    20. Juni 2014 um 18:17 Uhr

  • @jamesdlin: Mein Fehler. Tut mir leid. Ich wusste nicht, wo ich das gelesen habe.

    – Jack

    20. Juni 2014 um 18:22 Uhr

  • Aus irgendeinem Grund löst das Beispiel einen Fehler mit Hinweis aus n format specifies disabled. Was ist der Grund?

    – Johnny_D

    20. Juli 2014 um 19:46 Uhr

Benutzeravatar von Xzhsh
Xschsch

Ich habe nicht wirklich viele praktische Anwendungen in der realen Welt gesehen %n specifier, aber ich erinnere mich, dass es in alten printf-Schwachstellen mit a verwendet wurde Format-String-Angriff schon eine ganze Weile zurück.

Etwas, das so gelaufen ist

void authorizeUser( char * username, char * password){

    ...code here setting authorized to false...
    printf(username);

    if ( authorized ) {
         giveControl(username);
    }
}

wo ein böswilliger Benutzer den Benutzernamen-Parameter ausnutzen könnte, der als Formatzeichenfolge an printf übergeben wird, und eine Kombination von verwenden könnte %d, %c oder wir gehen durch den Aufrufstapel und ändern dann die autorisierte Variable auf einen wahren Wert.

Ja, es ist eine esoterische Verwendung, aber immer nützlich zu wissen, wenn man einen Daemon schreibt, um Sicherheitslücken zu vermeiden? 😀

  • Es gibt mehr Gründe als %n um die Verwendung einer ungeprüften Eingabezeichenfolge als zu vermeiden printf Zeichenfolge formatieren.

    – Keith Thompson

    20. Juni 2014 um 0:44 Uhr

Benutzeravatar von KLee1
KLee1

Aus hier wir sehen, dass es die Anzahl der bisher gedruckten Zeichen speichert.

n Das Argument soll ein Zeiger auf eine ganze Zahl sein, in die die Anzahl der Bytes geschrieben wird, die bisher durch diesen Aufruf an einen der ausgegeben wurden fprintf() Funktionen. Es wird kein Argument konvertiert.

Eine beispielhafte Verwendung wäre:

int n_chars = 0;
printf("Hello, World%n", &n_chars);

n_chars hätte dann einen Wert von 12.

R.. GitHub STOP HELPING ICEs Benutzeravatar
R.. GitHub HÖREN SIE AUF, ICE ZU HELFEN

Bisher beziehen sich alle Antworten darauf %n tut, aber nicht, warum irgendjemand es überhaupt wollen würde. Ich finde es ist etwas nützlich mit sprintf/snprintf, wenn Sie die resultierende Zeichenfolge möglicherweise später aufteilen oder ändern müssen, da der gespeicherte Wert ein Array-Index in die resultierende Zeichenfolge ist. Diese Anwendung ist jedoch viel nützlicher sscanfzumal Funktionen in der scanf Familie gibt nicht die Anzahl der verarbeiteten Zeichen zurück, sondern die Anzahl der Felder.

Eine weitere wirklich hackige Verwendung besteht darin, kostenlos ein Pseudo-Log10 zu erhalten, während gleichzeitig eine Nummer als Teil einer anderen Operation gedruckt wird.

  • +1 für die Erwähnung von Verwendungen für %n, obwohl ich bei “alle Antworten …” anderer Meinung bin. =P

    – jamesdlin

    4. August 2010 um 17:25 Uhr

  • Die bösen Jungs danken Ihnen für Ihre Verwendung von printf/%n, sprintf und sscanf 😉

    – jww

    9. Juli 2013 um 4:00 Uhr

  • @noloader: Wieso? Gebrauch von %n hat absolut keine Gefahr der Anfälligkeit für einen Angreifer. Die unangebrachte Schande von %n gehört wirklich zu der dummen Praxis, als Formatargument einen Nachrichtenstring statt einen Formatstring zu übergeben. Diese Situation tritt natürlich nie auf, wenn %n ist tatsächlich Teil einer absichtlich verwendeten Formatzeichenfolge.

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

    9. Juli 2013 um 4:06 Uhr


  • %n erlaubt Ihnen, in den Speicher zu schreiben. Ich denke, Sie gehen davon aus, dass der Angreifer diesen Zeiger nicht kontrolliert (ich könnte mich irren). Wenn der Angreifer den Zeiger kontrolliert (es ist nur ein weiterer Parameter für printf), könnte er/sie einen Schreibvorgang von 4 Bytes durchführen. Ob er davon profitieren kann, ist eine andere Geschichte.

    – jww

    9. Juli 2013 um 5:00 Uhr


  • @noloader: Das gilt für jede Verwendung von Zeigern. Niemand sagt “Bösewichte danke” für das Schreiben *p = f();. Weshalb sollte %ndie nur eine andere Art ist, ein Ergebnis in das Objekt zu schreiben, auf das ein Zeiger zeigt, als “gefährlich” angesehen werden, anstatt den Zeiger selbst als gefährlich zu betrachten?

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

    9. Juli 2013 um 5:47 Uhr

Benutzeravatar von Ardent Coder
Leidenschaftlicher Programmierer

Das Argument im Zusammenhang mit der %n wird als behandelt int* und wird mit der Gesamtzahl der an diesem Punkt gedruckten Zeichen gefüllt printf.

  • +1 für die Erwähnung von Verwendungen für %n, obwohl ich bei “alle Antworten …” anderer Meinung bin. =P

    – jamesdlin

    4. August 2010 um 17:25 Uhr

  • Die bösen Jungs danken Ihnen für Ihre Verwendung von printf/%n, sprintf und sscanf 😉

    – jww

    9. Juli 2013 um 4:00 Uhr

  • @noloader: Wieso? Gebrauch von %n hat absolut keine Gefahr der Anfälligkeit für einen Angreifer. Die unangebrachte Schande von %n gehört wirklich zu der dummen Praxis, als Formatargument einen Nachrichtenstring statt einen Formatstring zu übergeben. Diese Situation tritt natürlich nie auf, wenn %n ist tatsächlich Teil einer absichtlich verwendeten Formatzeichenfolge.

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

    9. Juli 2013 um 4:06 Uhr


  • %n erlaubt Ihnen, in den Speicher zu schreiben. Ich denke, Sie gehen davon aus, dass der Angreifer diesen Zeiger nicht kontrolliert (ich könnte mich irren). Wenn der Angreifer den Zeiger kontrolliert (es ist nur ein weiterer Parameter für printf), könnte er/sie einen Schreibvorgang von 4 Bytes durchführen. Ob er davon profitieren kann, ist eine andere Geschichte.

    – jww

    9. Juli 2013 um 5:00 Uhr


  • @noloader: Das gilt für jede Verwendung von Zeigern. Niemand sagt “Bösewichte danke” für das Schreiben *p = f();. Weshalb sollte %ndie nur eine andere Art ist, ein Ergebnis in das Objekt zu schreiben, auf das ein Zeiger zeigt, als “gefährlich” angesehen werden, anstatt den Zeiger selbst als gefährlich zu betrachten?

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

    9. Juli 2013 um 5:47 Uhr

Benutzeravatar der Community
Gemeinschaft

Neulich fand ich mich in einer Situation, wo %n würde mein Problem gut lösen. Im Gegensatz zu meiner früheren Antwort kann ich in diesem Fall keine gute Alternative finden.

Ich habe ein GUI-Steuerelement, das einen bestimmten Text anzeigt. Dieses Steuerelement kann einen Teil dieses Textes fett (oder kursiv oder unterstrichen usw.) anzeigen, und ich kann angeben, welcher Teil durch Angabe von Anfangs- und Endzeichenindizes angegeben wird.

In meinem Fall generiere ich den Text zum Steuerelement mit snprintf, und ich möchte, dass eine der Ersetzungen fett gedruckt wird. Das Finden der Start- und Endindizes für diese Substitution ist nicht trivial, weil:

  • Die Zeichenfolge enthält mehrere Ersetzungen, und eine der Ersetzungen ist ein beliebiger, benutzerdefinierter Text. Dies bedeutet, dass eine Textsuche nach der Substitution, die mir wichtig ist, möglicherweise mehrdeutig ist.

  • Die Formatzeichenfolge ist möglicherweise lokalisiert und verwendet möglicherweise die $ POSIX-Erweiterung für Positionsformatbezeichner. Daher ist das Durchsuchen der ursprünglichen Formatzeichenfolge nach den Formatbezeichnern selbst nicht trivial.

  • Der Lokalisierungsaspekt bedeutet auch, dass ich die Formatzeichenfolge nicht einfach in mehrere Aufrufe von aufteilen kann snprintf.

Daher wäre der einfachste Weg, die Indizes um eine bestimmte Substitution herum zu finden, Folgendes zu tun:

char buf[256];
int start;
int end;

snprintf(buf, sizeof buf,
         "blah blah %s %f yada yada %n%s%n yakety yak",
         someUserSpecifiedString,
         someFloat,
         &start, boldString, &end);
control->set_text(buf);
control->set_bold(start, end);

  • Ich gebe Ihnen +1 für den Anwendungsfall. Aber Sie werden eine Prüfung nicht bestehen, also sollten Sie wahrscheinlich eine andere Möglichkeit finden, den Anfang und das Ende des fettgedruckten Textes zu markieren. Es scheint wie drei snprintf während die Überprüfung der Rückgabewerte seither einwandfrei funktioniert snprintf gibt die Anzahl der geschriebenen Zeichen zurück. Vielleicht so etwas wie: int begin = snprintf(..., "blah blah %s %f yada yada", ...); und int end = snprintf(..., "%s", ...); und dann der Schwanz: snprintf(..., "blah blah");.

    – jww

    10. Juni 2017 um 4:24 Uhr


  • @jww Das Problem mit mehreren snprintf Aufrufe ist, dass die Ersetzungen in anderen Orten möglicherweise neu angeordnet werden, sodass sie nicht so aufgelöst werden können.

    – jamesdlin

    10. Juni 2017 um 6:04 Uhr

  • Danke für das Beispiel. Aber könnten Sie nicht etwa eine Terminal-Steuersequenz schreiben, um die Ausgabe direkt vor dem Feld fett darzustellen, und dann eine Sequenz danach schreiben? Wenn Sie die Terminal-Steuersequenzen nicht fest codieren, können Sie sie auch positionell (reorderable) machen.

    – PSkočik

    24. Juni 2019 um 11:08 Uhr

  • @PSkocik Wenn Sie geben an ein Terminal aus. Wenn Sie beispielsweise mit einem Win32-Rich-Edit-Steuerelement arbeiten, hilft das nicht, es sei denn, Sie möchten später zurückgehen und die Terminal-Steuersequenzen analysieren. Das setzt auch voraus, dass Sie die Terminal-Steuersequenzen im Rest des ersetzten Textes berücksichtigen möchten; Wenn Sie dies nicht tun, müssen Sie diese filtern oder entkommen. Ich sage nicht, dass es unmöglich ist, darauf zu verzichten %n; Ich behaupte das mit %n ist einfacher als Alternativen.

    – jamesdlin

    24. Juni 2019 um 15:35 Uhr

1424130cookie-checkWozu dient der Formatbezeichner %n in C?

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

Privacy policy