¿Cuál es el uso del especificador de formato %n en C?


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

 105
Author: Cristian Ciupitu, 2010-08-04

10 answers

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

#include <stdio.h>

int main()
{
  int val;

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

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

  return 0;

}

El código anterior imprime:

blah  blah
val = 5
 133
Author: Starkey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-21 17:29:35

La mayoría de estas respuestas explican lo que %n lo hace (que es no imprimir nada y escribir el número de caracteres impresos hasta ahora en una variable int), pero hasta ahora nadie ha dado realmente un ejemplo de lo que usa tiene. Aquí hay uno:

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

Se imprimirá:

hello: Foo
       Bar

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

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

Si la conveniencia ligeramente añadida vale la pena usar algo esotérico como %n (y posiblemente introducir errores) está abierto a debate.)

 163
Author: jamesdlin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-12 22:06:51

Realmente no he visto muchos usos prácticos en el mundo real del especificador %n, pero recuerdo que se usó en vulnerabilidades de printf oldschool con un ataque de cadena de formato hace bastante tiempo.

Algo que fue así

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

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

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

Donde un usuario malicioso podría aprovechar el parámetro username que se pasa a printf como la cadena de formato y usar una combinación de %d, %c o w / e para ir a través de la pila de llamadas y luego modificar la variable autorizado a un valor verdadero.

Sí, es un uso esotérico, pero siempre es útil saber cuando se escribe un demonio para evitar agujeros de seguridad? : D

 14
Author: Xzhsh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-21 11:45:24

Desde aquí vemos que almacena el número de caracteres impresos hasta ahora.

n El argumento será un puntero a un entero en el que se escribe el número de bytes escritos en la salida hasta ahora por esta llamada a una de las funciones fprintf(). No se convierte ningún argumento.

Un ejemplo de uso sería:

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

n_chars entonces tendría un valor de 12.

 13
Author: KLee1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-26 23:03:35

El argumento asociado con %n se tratará como un int* y se rellenará con el número total de caracteres impresos en ese punto en el printf.

 9
Author: Evan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-03 22:15:13

Hasta ahora todas las respuestas son acerca de que %n lo hace, pero no por qué alguien lo querría en primer lugar. Me parece algo útil con sprintf/snprintf, cuando es posible que necesite romper o modificar más tarde la cadena resultante, ya que el valor almacenado es un índice de matriz en la cadena resultante. Esta aplicación es mucho más útil, sin embargo, 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 realmente el uso de lame es obtener un pseudo-log10 de forma gratuita al mismo tiempo que se imprime un número como parte de otra operación.

 5
Author: R..,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-04 12:16:04

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

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

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

  • La cadena contiene varias sustituciones, y una de ellas es 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 el formato posicional prescriptores. Por lo tanto, buscar los especificadores de formato en la cadena de formato original no es trivial.

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

Por lo tanto, la forma más sencilla de encontrar los índices alrededor de una sustitución en particular sería 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);
 2
Author: jamesdlin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:26

No imprime nada. Se utiliza para averiguar cuántos caracteres se imprimieron antes de que %n apareciera en la cadena de formato, y enviarlo al 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;
}

(Documentación para _set_printf_count_output)

 1
Author: Merlyn Morgan-Graham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-21 11:48:35

Almacenará el valor del número de caracteres impresos hasta ahora 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
 1
Author: Sudhanshu Mishra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-21 11:49:40

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

 -5
Author: user411313,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-04 22:33:25