2010-08-03 20 views
98

Was ist die Verwendung des Formatbezeichners %n in C? Kann jemand mit einem Beispiel erklären?Was ist der% n Formatbezeichner in C?

+21

Was der feinen Kunst des Lesens das Handbuch geworden ist? – Jens

+0

Ich denke, die wirkliche Frage ist, was ist der ** POINT ** einer solchen Option? Warum würde jemand den Wert der gedruckten Zahlen wissen wollen, schreiben Sie viel weniger diesen Wert direkt in den Speicher. Es war, als ob die Entwickler gelangweilt waren und beschlossen, einen Fehler in den Kernel einzuführen. –

Antwort

122

Nichts gedruckt. Das Argument muss ein Zeiger auf einen vorzeichenbehafteten int sein, in dem die Anzahl der bisher geschriebenen Zeichen gespeichert ist.

#include <stdio.h> 

int main() 
{ 
    int val; 

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

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

    return 0; 

} 

vorheriger Code druckt:

blah blah 
val = 5 
+3

Gibt +1, wenn Sie Beispielcode hinzufügen. –

+1

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

+0

Können Sie erklären, warum es ein Zeiger sein muss? –

9

das Argument mit dem% n verbunden ist, wird in der printf an diesem Punkt gedruckt als int * und gefüllt mit der Anzahl der Gesamt Zeichen behandelt werden.

13

Von here sehen wir, dass es die Anzahl der bisher gedruckten Zeichen speichert.

n Das Argument wird ein Zeiger auf eine Ganzzahl sein, in dem die Anzahl der Bytes, die durch diesen Aufruf zu einer der fprintf() Funktionen so weit an den Ausgang geschrieben wird, geschrieben. Kein Argument wird konvertiert.

wäre ein Beispiel für die Verwendung sein:

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

n_chars würde dann einen Wert von 12 haben.

1

Es wird nichts gedruckt. Es wird, um herauszufinden, verwendet, wie viele Zeichen gedruckt wurde, bevor %n im Format-String erschienen und Ausgabe, die an die angegebene int:

#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); 
    return 0; 
} 

(Documentation for _set_printf_count_output)

12

Ich habe nicht wirklich viele gesehen praktische reale Welt verwendet die %n Spezifizierer, aber ich erinnere mich, dass es in oldschool printf Verwundbarkeiten mit einer format string attack eine ganze Weile zurück verwendet wurde.

Etwas, das wie diese ging

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 der Benutzerparameter nehmen könnte immer in printf als Format-String übergeben und eine Kombination aus %d verwenden, %c oder w/e, um durch der Aufruf-Stack und dann ändern Sie die Variable zu einem wahren Wert autorisiert.

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

+5

printf (Benutzername) fragt nach Formatzeichenfolgeangriff. – Nyan

+2

Nun ja, das ist der Punkt: P – Xzhsh

+0

Es gibt mehr Gründe als '% n' zu vermeiden, eine ungeprüfte Eingabezeichenfolge als 'printf'-Formatzeichenfolge zu verwenden. –

158

Die meisten dieser Antworten erklären, was %ntut (die nichts zu drucken ist und die Anzahl der Zeichen so weit zu einer int Variable gedruckt zu schreiben), aber bisher hat niemand wirklich ein Beispiel dessen, was gegeben verwenden Sie es hat.Hier ist eine:

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

druckt:

hello: Foo 
     Bar 

mit Foo und Bar ausgerichtet sind. (Es ist trivial, das zu tun, ohne %n für dieses Beispiel verwendet wird, und in der Regel konnte man immer bricht den ersten printf Aufruf:

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

Ob die etwas mehr Komfort ist etwas wert esoterisch wie %n mit (und möglicherweise der Einführung Fehler) ist offen für Diskussionen.)

+3

Oh my - das ist eine zeichenbasierte Version der Berechnung der Pixelgröße von String in einer bestimmten Schriftart! – Arkadiy

+0

Können Sie erklären, warum & n und * s benötigt werden? Sind sie beide Zeiger? –

+8

@AndrewS '& n' ist ein Zeiger (' & 'ist der Address-of-Operator); Ein Zeiger ist notwendig, da C ein Vorübergehender Wert ist, und ohne einen Zeiger konnte "printf" den Wert von "n" nicht ändern. Die Verwendung von '% s' in der 'printf'-Formatzeichenfolge gibt einen '% s'-Spezifizierer (in diesem Fall die leere Zeichenkette' '' ') unter Verwendung einer * Feldbreite * von' n'-Zeichen aus. Eine Erklärung der grundlegenden 'printf'-Prinzipien liegt grundsätzlich außerhalb des Umfangs dieser Frage (und der Antwort); Ich würde empfehlen, die 'printf'-Dokumentation zu lesen oder eine eigene Frage zu SO zu stellen. – jamesdlin

5

Bis jetzt sind alle Antworten darüber, dass %n tut, aber nicht, warum jemand es in erster Linie wollen würde. Ich finde es mit sprintf/snprintf etwas nützlich, wenn Sie später möglicherweise die resultierende Zeichenfolge aufteilen oder ändern müssen, da der gespeicherte Wert ein Array-Index in der resultierenden Zeichenfolge ist. Diese Anwendung ist jedoch mit sscanf viel nützlicher, insbesondere, da Funktionen in der scanf-Familie die Anzahl der verarbeiteten Zeichen nicht zurückgeben, sondern die Anzahl der Felder.

Eine andere wirklich lahme Verwendung ist ein Pseudo-log10 zur gleichen Zeit frei zu bekommen, während eine Nummer als Teil einer anderen Operation gedruckt wird.

+0

+1 für die Verwendung von '% n', obwohl ich über" alle Antworten ... "differieren möchte. = P – jamesdlin

+1

Die bösen Jungs danken Ihnen für Ihre Verwendung von printf /% n, sprintf und sscanf;) – jww

+4

@noloader: Wie so? * Die Verwendung von * '% n' hat absolut keine Verletzungsgefahr für einen Angreifer.Die unangebrachte Infamie von '% n' gehört wirklich zur dummen Praxis, eine Nachrichtenkette anstelle einer Formatzeichenkette als Formatargument zu übergeben. Diese Situation tritt natürlich niemals auf, wenn "% n" tatsächlich Teil einer intentionalen Formatzeichenfolge ist, die verwendet wird. –

-5

% n ist C99, funktioniert nicht mit VC++.

+2

'% n' existierte in C89. Es funktioniert nicht mit MSVC, weil Microsoft es aus Sicherheitsgründen standardmäßig deaktiviert hat; Sie müssen zuerst _set_printf_count_output aufrufen, um es zu aktivieren. (Siehe Merlyn Morgan-Grahams Antwort.) – jamesdlin

+0

Nein, C89 definiert diese Funktion nicht/Backdoorbug. Siehe http://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628 ?? wo ist der URL-Tagger für Kommentare? – user411313

+4

Sie sind einfach falsch. Sie ist in Tabelle B-1 ("printf" -Umsetzungen) des Anhangs B von K & R, 2. Auflage, deutlich aufgeführt. (Seite 244 meiner Kopie.) Oder siehe Abschnitt 7.9.6.1 (Seite 134) der ISO C90-Spezifikation. – jamesdlin

0

Es speichert den Wert der Anzahl der bisher gedruckten Zeichen in dieser printf() Funktion.

Beispiel:

int a; 
printf("Hello World %n \n", &a); 
printf("Characters printed so far = %d",a); 

Die Ausgabe dieses Programms wird

Hello World 
Characters printed so far = 12 
+0

wenn ich versuche, Sie Code gibt es mir: Hello World Characters so weit gedruckt = 36 ,,, ,, warum 36?! Ich benutze einen 32bit GCC in einer Windows-Maschine. –

2

Der andere Tag fand ich mich in einer Situation, wo %n schön mein Problem lösen würde. Im Gegensatz zu my earlier answer 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 der Indexindizes für Anfang und Ende festgelegt wird.

In meinem Fall, Ich erzeuge den Text an die Steuerung mit snprintf, und ich würde eine der Substitutionen gerne fett gemacht werden. den Ausgangs Auffinden und Indizes auf diese Substitution endend ist nicht trivial, weil:

  • Der String mehr Substitutionen enthält, und eine der Substitutionen ist beliebig, vom Benutzer festgelegten Text. Dies bedeutet, dass eine textuelle Suche nach der Ersetzung, 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 Suchen der ursprünglichen Formatzeichenfolge für die Formatspezifizierer selbst nicht trivial.

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

Deshalb ist die einfachste Art und Weise die Indizes um eine bestimmte Substitution zu finden zu tun wäre:

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); 
+0

Ich gebe dir +1 für den Anwendungsfall. Aber Sie werden ein Audit nicht bestehen, also sollten Sie wahrscheinlich einen anderen Weg finden, um den Anfang und das Ende des fett gedruckten Textes zu markieren. Es scheint, als ob drei 'snprintf' beim Überprüfen von Rückgabewerten gut funktionieren, da' snprintf' die Anzahl der geschriebenen Zeichen zurückgibt. Vielleicht etwas wie: 'int begin = snprintf (...," bla bla% s% f yada yada ", ...);' und 'int end = snprintf (...,"% s ", ...); 'und dann der Schwanz:' snprintf (..., "blah blah"); '. – jww

+0

@jww Das Problem mit mehreren 'snprintf'-Aufrufen besteht darin, dass die Ersetzungen möglicherweise in anderen Gebietsschemas neu angeordnet werden, sodass sie nicht so aufgelöst werden können. – jamesdlin

Verwandte Themen