Was nützt die %n
Formatbezeichner in C? Könnte jemand mit einem Beispiel erklären?
Wozu dient der Formatbezeichner %n in C?
Josch
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 ändernn
. Das%*s
Verwendung in derprintf
format string druckt a%s
Bezeichner (in diesem Fall die leere Zeichenfolge""
) Verwendung einer Feldbreite vonn
Figuren. Eine grundlegende Erklärungprintf
Grundsätze liegt im Grunde außerhalb des Rahmens dieser Frage (und Antwort); Ich würde empfehlen, die zu lesenprintf
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
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
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 vermeidenprintf
Zeichenfolge formatieren.– Keith Thompson
20. Juni 2014 um 0:44 Uhr
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 wurdenfprintf()
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 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 sscanf
zumal 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%n
die 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
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%n
die 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
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 funktioniertsnprintf
gibt die Anzahl der geschriebenen Zeichen zurück. Vielleicht so etwas wie:int begin = snprintf(..., "blah blah %s %f yada yada", ...);
undint 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
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
machtprintf
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