Was ist aus der schönen Kunst geworden, das schöne Handbuch zu lesen?
Jens
8
Ich denke, die eigentliche Frage ist, was ist der Punkt einer solchen Option? Warum sollte jemand den Wert der gedruckten Zeichenzahlen wissen wollen und diesen Wert direkt in den Speicher schreiben? Es war, als wären die Entwickler gelangweilt und beschlossen,
Nichts gedruckt. Das Argument muss ein Zeiger auf ein vorzeichenbehaftetes int sein, in dem die Anzahl der bisher geschriebenen Zeichen gespeichert ist.
Sie erwähnen, dass das Argument ein Zeiger auf ein vorzeichenbehaftetes int sein muss, dann haben Sie in Ihrem Beispiel ein vorzeichenloses int verwendet (wahrscheinlich nur ein Tippfehler).
bta
1
@ Andrews: Weil die Funktion den Wert der Variablen ändert.
Jack
3
@ Jack intist immer signiert.
Jamesdlin
1
@ Jamesdlin: Mein Fehler. Es tut mir leid. Ich wusste nicht, wo ich das gelesen habe.
Jack
1
Aus irgendeinem Grund löst das Beispiel einen Fehler mit Hinweis aus n format specifies disabled. Was ist der Grund?
Johnny_D
186
Die meisten dieser Antworten erklären, was zu tun ist ( was %nbedeutet , nichts zu drucken und die Anzahl der bisher gedruckten Zeichen in eine intVariable zu schreiben ), aber bisher hat niemand wirklich ein Beispiel dafür gegeben, welchen Nutzen es hat. Hier ist eine:
int n;
printf("%s: %nFoo\n","hello",&n);
printf("%*sBar\n", n,"");
wird drucken:
hello:FooBar
mit Foo und Bar ausgerichtet. (Es ist trivial, dies zu tun, ohne %ndieses spezielle Beispiel zu verwenden, und im Allgemeinen könnte man diesen ersten printfAufruf immer abbrechen :
int n = printf("%s: ","hello");
printf("Foo\n");
printf("%*sBar\n", n,"");
Ob es sich lohnt, etwas Esoterisches wie die leicht hinzugefügte Bequemlichkeit zu verwenden %n(und möglicherweise Fehler einzuführen), kann diskutiert werden.)
Oh mein Gott - dies ist eine zeichenbasierte Version der Berechnung der Pixelgröße eines Strings in einer bestimmten Schriftart!
Können Sie erklären, warum & n und * s benötigt werden? Sind sie beide Zeiger?
Andrew S
9
@AndrewS &nist ein Zeiger ( &ist die Adresse des Operators); Ein Zeiger ist erforderlich, da C ein Wert ist und ohne Zeiger printfden Wert von nicht ändern kann n. Die %*sVerwendung in der printfFormatzeichenfolge druckt einen %sBezeichner (in diesem Fall die leere Zeichenfolge "") unter Verwendung einer Feldbreite von nZeichen. Eine Erklärung der printfGrundprinzipien liegt grundsätzlich außerhalb des Rahmens dieser Frage (und Antwort); Ich würde empfehlen, die printfDokumentation zu lesen oder Ihre eigene Frage zu SO zu stellen.
Jamesdlin
3
Vielen Dank, dass Sie einen Anwendungsfall gezeigt haben. Ich verstehe nicht, warum die Leute das Handbuch einfach kopieren, in SO einfügen und es manchmal umformulieren. Wir sind Menschen und alles geschieht aus einem Grund, der immer in einer Antwort erklärt werden sollte. "Tut nichts" ist wie zu sagen "Das Wort cool bedeutet cool" - fast nutzloses Wissen.
the_endian
1
@PSkocik Es ist kompliziert und fehleranfällig genug, ohne eine zusätzliche Indirektionsebene hinzuzufügen.
Jamesdlin
18
Ich habe nicht wirklich viele praktische Anwendungen des %nSpezifizierers in der realen Welt gesehen , aber ich erinnere mich, dass er vor einiger Zeit bei Druckschwachstellen der alten Schule mit einem Format-String-Angriff verwendet wurde .
Etwas, das so lief
void authorizeUser(char* username,char* password){...code here setting authorized to false...
printf(username);if( authorized ){
giveControl(username);}}
wo ein böswilliger Benutzer die Vorteile des Benutzernamens nehmen könnte Parameter in printf als Format - String übergeben zu werden und eine Kombination aus verwenden %d, %coder w / e durch den Call - Stack zu gehen und dann die Variable ändern , um einen wahren Wert zugelassen.
Ja, es ist eine esoterische Verwendung, aber immer nützlich, wenn Sie einen Daemon schreiben, um Sicherheitslücken zu vermeiden? : D.
Es gibt mehr Gründe, als %ndie Verwendung einer nicht aktivierten Eingabezeichenfolge als Formatzeichenfolge zu vermeiden printf.
Keith Thompson
13
Von hier aus sehen wir, dass die Anzahl der bisher gedruckten Zeichen gespeichert wird.
n Das Argument soll ein Zeiger auf eine Ganzzahl sein, in die die Anzahl der Bytes geschrieben wird, die bisher durch diesen Aufruf einer der fprintf()Funktionen in die Ausgabe geschrieben wurden . Es wird kein Argument konvertiert.
Eine beispielhafte Verwendung wäre:
int n_chars =0;
printf("Hello, World%n",&n_chars);
Bisher geht es bei allen Antworten darum %n, aber nicht darum, warum irgendjemand es überhaupt wollen würde. Ich finde es etwas nützlich mit sprintf/ snprintf, wenn Sie die resultierende Zeichenfolge später auflösen oder ändern müssen, da der gespeicherte Wert ein Array-Index in der resultierenden Zeichenfolge ist. Diese Anwendung ist jedoch viel nützlicher, sscanfinsbesondere da Funktionen in der scanfFamilie nicht die Anzahl der verarbeiteten Zeichen, sondern die Anzahl der Felder zurückgeben.
Eine andere wirklich hackige Verwendung besteht darin, gleichzeitig ein Pseudo-Log10 kostenlos zu erhalten, während eine Nummer als Teil einer anderen Operation gedruckt wird.
+1 für die Erwähnung von Verwendungen für %n, obwohl ich mich über "alle Antworten ..." unterscheiden möchte. = P
Jamesdlin
1
Die bösen Jungs danken Ihnen für Ihre Verwendung von printf /% n, sprintf und sscanf;)
jww
6
@noloader: Wie so? Die Verwendung von%n hat absolut keine Gefahr der Verwundbarkeit für einen Angreifer. Die verlegte Schande von %ngehört wirklich zu der dummen Praxis, eine Nachrichtenzeichenfolge anstelle einer Formatzeichenfolge als Formatargument zu übergeben. Diese Situation tritt natürlich nie auf, wenn %ntatsächlich Teil einer beabsichtigten Formatzeichenfolge verwendet wird.
R .. GitHub STOP HELPING ICE
Mit% n können Sie in den Speicher schreiben. Ich denke, Sie gehen davon aus, dass der Angreifer diesen Zeiger nicht kontrolliert (ich könnte mich irren). Wenn der Angreifer den Zeiger steuert (es ist nur ein weiterer Parameter für printf), kann er einen Schreibvorgang von 4 Bytes ausführen. Ob er / sie profitieren kann, ist eine andere Geschichte.
JWW
8
@noloader: Das gilt für jede Verwendung von Zeigern. Niemand sagt "böse Jungs, danke" fürs Schreiben *p = f();. Warum sollte %n, was nur eine andere Art ist, ein Ergebnis auf 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 STOP HELPING ICE
10
Das mit dem verknüpfte Argument %nwird als behandelt int*und mit der Anzahl der an diesem Punkt im Feld gedruckten Gesamtzeichen gefüllt printf.
Neulich befand ich mich in einer Situation, in %nder mein Problem gut gelöst werden konnte. 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 Start- und Endzeichenindizes angegeben wird.
In meinem Fall generiere ich den Text für das Steuerelement mit snprintfund 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 beliebiger, benutzerdefinierter Text. Dies bedeutet, dass eine Textsuche nach der Substitution, die mir wichtig ist, möglicherweise nicht eindeutig ist.
Die Formatzeichenfolge ist möglicherweise lokalisiert und verwendet möglicherweise die $POSIX-Erweiterung für Positionsformatspezifizierer. Daher ist das Durchsuchen der ursprünglichen Formatzeichenfolge nach den Formatspezifizierern selbst nicht trivial.
Der Lokalisierungsaspekt bedeutet auch, dass ich die Formatzeichenfolge nicht einfach in mehrere Aufrufe an aufteilen kann snprintf.
Daher ist es am einfachsten, die Indizes für eine bestimmte Substitution zu finden:
Ich gebe Ihnen +1 für den Anwendungsfall. Sie werden jedoch ein Audit nicht bestehen. Daher sollten Sie wahrscheinlich einen anderen Weg finden, um den Anfang und das Ende des fett gedruckten Textes zu markieren. Es scheint, als würden drei snprintfbeim Überprüfen der Rückgabewerte einwandfrei funktionieren, da snprintfdie Anzahl der geschriebenen Zeichen zurückgegeben wird. Vielleicht so etwas wie: int begin = snprintf(..., "blah blah %s %f yada yada", ...);und int end = snprintf(..., "%s", ...);dann der Schwanz: snprintf(..., "blah blah");.
JWW
3
@jww Das Problem bei mehreren snprintfAufrufen besteht darin, dass die Ersetzungen möglicherweise in anderen Gebietsschemas neu angeordnet werden, sodass sie nicht so aufgeteilt werden können.
Jamesdlin
Danke für das Beispiel. Aber könnten Sie nicht eine Terminalsteuerungssequenz schreiben, um die Ausgabe direkt vor dem Feld fett zu machen, und dann eine Sequenz danach schreiben? Wenn Sie die Terminalsteuerungssequenzen nicht fest codieren, können Sie sie auch positionell (nachbestellbar) machen.
PSkocik
1
@PSkocik Wenn Sie an ein Terminal ausgeben. Wenn Sie beispielsweise mit einem Win32-Rich-Edit-Steuerelement arbeiten, hilft dies nur, wenn Sie die Terminalsteuerungssequenzen anschließend erneut analysieren möchten. Dies setzt auch voraus, dass Sie Terminalsteuerungssequenzen im Rest des ersetzten Textes berücksichtigen möchten. Wenn Sie dies nicht tun, müssten Sie diese filtern oder entkommen. Ich sage nicht, dass es unmöglich ist, darauf zu verzichten %n. Ich behaupte, dass die Verwendung %neinfacher ist als Alternativen.
Jamesdlin
1
Es wird nichts gedruckt. Es wird verwendet, um herauszufinden, wie viele Zeichen gedruckt wurden, bevor %nsie in der Formatzeichenfolge angezeigt wurden, und um diese an das angegebene int auszugeben:
#include<stdio.h>int main(int argc,char* argv[]){int resultOfNSpecifier =0;
_set_printf_count_output(1);/* Required in visual studio */
printf("Some format string%n\n",&resultOfNSpecifier);
printf("Count of chars before the %%n: %d\n", resultOfNSpecifier);return0;}
Wenn ich versuche, Sie zu codieren, gibt es mir: Hallo Weltzeichen, die bisher gedruckt wurden = 36 ,,,,, warum 36 ?! Ich verwende ein 32-Bit-GCC in einem Windows-Computer.
%nexistierte in C89. Es funktioniert nicht mit MSVC, da Microsoft es aus Sicherheitsgründen standardmäßig deaktiviert hat. Sie müssen _set_printf_count_outputzuerst aufrufen , um es zu aktivieren. (Siehe die Antwort von Merlyn Morgan-Graham.)
Du liegst einfach falsch. Es ist in Tabelle B-1 ( printfUmrechnungen) von Anhang B von K & R, 2. Auflage, klar aufgeführt . (Seite 244 meiner Kopie.) Oder siehe Abschnitt 7.9.6.1 (Seite 134) der ISO C90-Spezifikation.
%n
Marken ausprintf
Versehen Turing-complete und Sie können zB implementieren Brainfuck darin finden github.com/HexHive/printbf und oilshell.org/blog/2019/02/07.html#appendix-a-minor-sublanguagesAntworten:
Nichts gedruckt. Das Argument muss ein Zeiger auf ein vorzeichenbehaftetes int sein, in dem die Anzahl der bisher geschriebenen Zeichen gespeichert ist.
Der vorherige Code druckt:
quelle
int
ist immer signiert.n format specifies disabled
. Was ist der Grund?Die meisten dieser Antworten erklären, was zu tun ist ( was
%n
bedeutet , nichts zu drucken und die Anzahl der bisher gedruckten Zeichen in eineint
Variable zu schreiben ), aber bisher hat niemand wirklich ein Beispiel dafür gegeben, welchen Nutzen es hat. Hier ist eine:wird drucken:
mit Foo und Bar ausgerichtet. (Es ist trivial, dies zu tun, ohne
%n
dieses spezielle Beispiel zu verwenden, und im Allgemeinen könnte man diesen erstenprintf
Aufruf immer abbrechen :Ob es sich lohnt, etwas Esoterisches wie die leicht hinzugefügte Bequemlichkeit zu verwenden
%n
(und möglicherweise Fehler einzuführen), kann diskutiert werden.)quelle
&n
ist ein Zeiger (&
ist die Adresse des Operators); Ein Zeiger ist erforderlich, da C ein Wert ist und ohne Zeigerprintf
den Wert von nicht ändern kannn
. Die%*s
Verwendung in derprintf
Formatzeichenfolge druckt einen%s
Bezeichner (in diesem Fall die leere Zeichenfolge""
) unter Verwendung einer Feldbreite vonn
Zeichen. Eine Erklärung derprintf
Grundprinzipien liegt grundsätzlich außerhalb des Rahmens dieser Frage (und Antwort); Ich würde empfehlen, dieprintf
Dokumentation zu lesen oder Ihre eigene Frage zu SO zu stellen.Ich habe nicht wirklich viele praktische Anwendungen des
%n
Spezifizierers in der realen Welt gesehen , aber ich erinnere mich, dass er vor einiger Zeit bei Druckschwachstellen der alten Schule mit einem Format-String-Angriff verwendet wurde .Etwas, das so lief
wo ein böswilliger Benutzer die Vorteile des Benutzernamens nehmen könnte Parameter in printf als Format - String übergeben zu werden und eine Kombination aus verwenden
%d
,%c
oder w / e durch den Call - Stack zu gehen und dann die Variable ändern , um einen wahren Wert zugelassen.Ja, es ist eine esoterische Verwendung, aber immer nützlich, wenn Sie einen Daemon schreiben, um Sicherheitslücken zu vermeiden? : D.
quelle
%n
die Verwendung einer nicht aktivierten Eingabezeichenfolge als Formatzeichenfolge zu vermeidenprintf
.Von hier aus sehen wir, dass die Anzahl der bisher gedruckten Zeichen gespeichert wird.
Eine beispielhafte Verwendung wäre:
n_chars
hätte dann einen Wert von12
.quelle
Bisher geht es bei allen Antworten darum
%n
, aber nicht darum, warum irgendjemand es überhaupt wollen würde. Ich finde es etwas nützlich mitsprintf
/snprintf
, wenn Sie die resultierende Zeichenfolge später auflösen oder ändern müssen, da der gespeicherte Wert ein Array-Index in der resultierenden Zeichenfolge ist. Diese Anwendung ist jedoch viel nützlicher,sscanf
insbesondere da Funktionen in derscanf
Familie nicht die Anzahl der verarbeiteten Zeichen, sondern die Anzahl der Felder zurückgeben.Eine andere wirklich hackige Verwendung besteht darin, gleichzeitig ein Pseudo-Log10 kostenlos zu erhalten, während eine Nummer als Teil einer anderen Operation gedruckt wird.
quelle
%n
, obwohl ich mich über "alle Antworten ..." unterscheiden möchte. = P%n
hat absolut keine Gefahr der Verwundbarkeit für einen Angreifer. Die verlegte Schande von%n
gehört wirklich zu der dummen Praxis, eine Nachrichtenzeichenfolge anstelle einer Formatzeichenfolge als Formatargument zu übergeben. Diese Situation tritt natürlich nie auf, wenn%n
tatsächlich Teil einer beabsichtigten Formatzeichenfolge verwendet wird.*p = f();
. Warum sollte%n
, was nur eine andere Art ist, ein Ergebnis auf das Objekt zu schreiben, auf das ein Zeiger zeigt, als "gefährlich" angesehen werden, anstatt den Zeiger selbst als gefährlich zu betrachten?Das mit dem verknüpfte Argument
%n
wird als behandeltint*
und mit der Anzahl der an diesem Punkt im Feld gedruckten Gesamtzeichen gefülltprintf
.quelle
Neulich befand ich mich in einer Situation, in
%n
der mein Problem gut gelöst werden konnte. 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 Start- und Endzeichenindizes angegeben wird.
In meinem Fall generiere ich den Text für das Steuerelement mit
snprintf
und 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 beliebiger, benutzerdefinierter Text. Dies bedeutet, dass eine Textsuche nach der Substitution, die mir wichtig ist, möglicherweise nicht eindeutig ist.
Die Formatzeichenfolge ist möglicherweise lokalisiert und verwendet möglicherweise die
$
POSIX-Erweiterung für Positionsformatspezifizierer. Daher ist das Durchsuchen der ursprünglichen Formatzeichenfolge nach den Formatspezifizierern selbst nicht trivial.Der Lokalisierungsaspekt bedeutet auch, dass ich die Formatzeichenfolge nicht einfach in mehrere Aufrufe an aufteilen kann
snprintf
.Daher ist es am einfachsten, die Indizes für eine bestimmte Substitution zu finden:
quelle
snprintf
beim Überprüfen der Rückgabewerte einwandfrei funktionieren, dasnprintf
die Anzahl der geschriebenen Zeichen zurückgegeben wird. Vielleicht so etwas wie:int begin = snprintf(..., "blah blah %s %f yada yada", ...);
undint end = snprintf(..., "%s", ...);
dann der Schwanz:snprintf(..., "blah blah");
.snprintf
Aufrufen besteht darin, dass die Ersetzungen möglicherweise in anderen Gebietsschemas neu angeordnet werden, sodass sie nicht so aufgeteilt werden können.%n
. Ich behaupte, dass die Verwendung%n
einfacher ist als Alternativen.Es wird nichts gedruckt. Es wird verwendet, um herauszufinden, wie viele Zeichen gedruckt wurden, bevor
%n
sie in der Formatzeichenfolge angezeigt wurden, und um diese an das angegebene int auszugeben:( Dokumentation für
_set_printf_count_output
)quelle
Darin wird der Wert der Anzahl der bisher gedruckten Zeichen gespeichert
printf()
Funktion .Beispiel:
Die Ausgabe dieses Programms wird sein
quelle
% n ist C99, funktioniert nicht mit VC ++.
quelle
%n
existierte in C89. Es funktioniert nicht mit MSVC, da Microsoft es aus Sicherheitsgründen standardmäßig deaktiviert hat. Sie müssen_set_printf_count_output
zuerst aufrufen , um es zu aktivieren. (Siehe die Antwort von Merlyn Morgan-Graham.)printf
Umrechnungen) von Anhang B von K & R, 2. Auflage, klar aufgeführt . (Seite 244 meiner Kopie.) Oder siehe Abschnitt 7.9.6.1 (Seite 134) der ISO C90-Spezifikation._set_printf_count_output