2010-08-03 13 views
98

¿Cuál es el uso del especificador de formato %n en C? ¿Alguien podría explicar con un ejemplo?¿Cuál es el uso del especificador de formato% n en C?

+21

Qué ha sido del arte de la lectura del manual fina? – Jens

+0

Creo que la verdadera pregunta es ¿cuál es el ** PUNTO ** de una opción como esta? ¿Por qué alguien querría saber el valor de los números de caracteres impresos y mucho menos escribir ese valor directamente en la memoria? Era como si los desarrolladores se aburrieran y decidieran introducir un error en kernal –

Respuesta

122

Nada impreso. El argumento debe ser un puntero a un int firmado, donde se almacena el número de caracteres escritos hasta el momento.

#include <stdio.h> 

int main() 
{ 
    int val; 

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

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

    return 0; 

} 

Los anteriores impresiones de código:

blah blah 
val = 5 
+3

Darán +1 si agrega un código de ejemplo. –

+1

Menciona que el argumento debe ser un puntero a un int firmado, luego usó un int sin firmar en su ejemplo (probablemente solo un error tipográfico). – bta

+0

¿Puedes explicar por qué tiene que ser un puntero? –

9

El argumento asociado con el% n será tratado como un int * y se llena con el número de caracteres en total impresas en ese punto en el printf.

13

De here vemos que almacena el número de caracteres impresos hasta el momento.

n El argumento debe ser un puntero a un entero en el que está escrito el número de bytes escritos en la salida hasta el momento por esta llamada a uno de los fprintf() funciones. Ningún argumento se convierte.

Un ejemplo de uso sería:

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

n_chars entonces tener un valor de 12.

1

No imprime nada. Se utiliza para calcular cuántos caracteres se imprimieron antes %n apareció en la cadena de formato, y la salida que a la int proporcionado:

#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

realmente no he visto a muchos usos prácticos del mundo real del especificador %n, pero recuerdo que se usó en vulnerabilidades printf de la vieja escuela con un format string attack hace bastante tiempo.

Algo que fue como esto

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

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

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

donde un usuario malintencionado podría aprovechar el parámetro de nombre de usuario era superada en printf como la cadena de formato y utilizar una combinación de %d, %c o w/e ir a través de la pila de llamadas y luego modificar la variable autorizada a un valor verdadero.

Sí, es un uso esotérico, pero siempre es útil saber al escribir un daemon para evitar agujeros de seguridad. : D

+5

printf (nombre de usuario) solicita un ataque de cadena de formato. – Nyan

+2

Bueno, sí, ese es el punto: P – Xzhsh

+0

Hay más razones que '% n' para evitar el uso de una cadena de entrada sin marcar como una cadena de formato' printf'. –

158

La mayoría de estas respuestas explicar lo %nhace (que es para imprimir nada y escribir el número de caracteres impresos hasta el momento a una variable int), pero hasta ahora nadie ha dado un ejemplo de lo use tiene.Aquí está uno:

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

imprimirá:

hello: Foo 
     Bar 

con Foo y Bar alineados. (Es trivial para hacer eso sin usar %n para este ejemplo en particular, y en general uno siempre podía romper esa primera printf llamada:

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

Si la conveniencia ligeramente añadido es digno de usar algo esotérico como %n (y, posiblemente, la introducción de errores) está abierto al debate.)

+3

¡Oh, esto es una versión basada en caracteres de calcular el tamaño de píxel de la cadena en una fuente determinada! – Arkadiy

+0

Podría explicar por qué se necesitan n & * s. ¿Son ambos punteros? –

+8

@AndrewS '& n' es un puntero (' & 'es el operador de dirección-de); un puntero es necesario porque C es pass-by-value, y sin un puntero, 'printf' no puede modificar el valor de' n'. El uso de '% * s' en la cadena de formato' printf' imprime un especificador '% s' (en este caso, la cadena vacía' "" ') utilizando un * ancho de campo * de' n' caracteres. Una explicación de los principios básicos de 'printf' está básicamente fuera del alcance de esta pregunta (y respuesta); Recomiendo leer la documentación 'printf' o hacer tu propia pregunta en SO. – jamesdlin

5

Hasta ahora todas las respuestas se refieren a eso %n, pero no por qué alguien querría en primer lugar. Encuentro que es algo útil con sprintf/snprintf, cuando tal vez necesite romper o modificar la cadena resultante, ya que el valor almacenado es un índice de matriz en la cadena resultante. Sin embargo, esta aplicación es mucho más útil con sscanf, especialmente porque las funciones de la familia scanf no devuelven el número de caracteres procesados, sino el número de campos.

Otro uso realmente incorrecto es obtener un pseudo-log10 de forma gratuita al mismo tiempo mientras se imprime un número como parte de otra operación.

+0

+1 para mencionar usos para '% n', aunque me gustaría diferir sobre" todas las respuestas ... ". = P – jamesdlin

+1

Los chicos malos gracias por su uso de printf /% n, sprintf y sscanf;) – jww

+4

@noloader: ¿Cómo es eso? * El uso de * '% n' no tiene ningún peligro de vulnerabilidad para un atacante.La infamia fuera de lugar de '% n' realmente pertenece a la práctica estúpida de pasar una cadena de mensaje en lugar de una cadena de formato como argumento de formato. Esta situación, por supuesto, nunca surge cuando '% n' es realmente parte de una cadena de formato intencional que se utiliza. –

-5

% n es C99, funciona no con VC++.

+2

'% n' existía en C89. No funciona con MSVC porque Microsoft lo deshabilitó de forma predeterminada por cuestiones de seguridad; debe llamar '_set_printf_count_output' primero para habilitarlo. (Consulte la respuesta de Merlyn Morgan-Graham.) – jamesdlin

+0

No, C89 no define esta característica/puerta trasera. Consulte K & R + ANSI-C http://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628 ¿dónde está el URL-tagger para comentarios? – user411313

+4

Simplemente estás equivocado. Está listado claramente en la Tabla B-1 (conversiones 'printf') del Apéndice B de K & R, 2da edición. (Página 244 de mi copia). O consulte la sección 7.9.6.1 (página 134) de la especificación ISO C90. – jamesdlin

0

Almacenará el valor del número de caracteres impresos hasta el momento en esa función printf().

Ejemplo:

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

La salida de este programa será

Hello World 
Characters printed so far = 12 
+0

cuando intento codificarlo, me da: Hello World Characters printed hasta el momento = 36 ,,, ,, ¿por qué 36 ?! Uso un GCC de 32 bits en una máquina de Windows. –

2

El otro día me encontré en una situación en la %n podría muy bien resolver mi problema. A diferencia de my earlier answer, en este caso, no puedo idear una buena alternativa.

Tengo un control de GUI que muestra texto específico. Este control puede mostrar parte de ese texto en negrita (o en cursiva, o subrayado, etc.), y puedo especificar qué parte especificando los índices de caracteres iniciales y finales.

En mi caso, estoy generando el texto para el control con snprintf, y me gustaría que una de las sustituciones se haga en negrita. Encontrar el inicio y fin de los índices de esta sustitución no es trivial, ya que:

  • La cadena contiene múltiples sustituciones, y una de las sustituciones es un texto arbitrario, especificado por el usuario. Esto significa que hacer una búsqueda textual de la sustitución que me importa es potencialmente ambigua.

  • La cadena de formato puede estar localizada, y puede usar la extensión POSIX $ para especificadores de formato de posición. Por lo tanto, buscar la cadena de formato original para los especificadores de formato no es trivial.

  • El aspecto de localización también significa que no puedo dividir fácilmente la cadena de formato en varias llamadas al snprintf.

lo tanto, la forma más sencilla para encontrar los índices en torno a una sustitución particular habría que hacer:

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

Le daré +1 para el caso de uso. Pero fallará en una auditoría, por lo que probablemente deba idear otra forma de marcar el comienzo y el final del texto en negrita. Parece que tres 'snprintf' mientras se comprueban los valores de retorno funcionarán bien, ya que' snprintf' devuelve la cantidad de caracteres escritos. Tal vez algo como: 'int begin = snprintf (...," blah blah% s% f yada yada ", ...);' y 'int end = snprintf (...,"% s ", ...); 'y luego la cola:' snprintf (..., "blah blah"); '. – jww

+0

@jww El problema con múltiples llamadas 'snprintf' es que las sustituciones pueden reorganizarse en otras configuraciones regionales, por lo que no se pueden dividir así. – jamesdlin

Cuestiones relacionadas