web-dev-qa-db-fra.com

Quelle est l'utilisation du spécificateur de format% n en C?

Quelle est l'utilisation de la %n spécificateur de format en C? Quelqu'un pourrait-il expliquer avec un exemple?

116
josh

Rien imprimé. L'argument doit être un pointeur sur un entier signé, où le nombre de caractères écrits jusqu'à présent est stocké.

#include <stdio.h>

int main()
{
  int val;

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

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

  return 0;

}

Le code précédent est imprimé:

blah  blah
val = 5
142
Starkey

La plupart de ces réponses expliquent ce que %nest (qui consiste à ne rien imprimer et à écrire le nombre de caractères imprimés jusqu’à présent dans une variable int), mais jusqu’à présent personne n’a vraiment donné d’exemple de ce que - tiliser il a. En voici un:

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

imprimera:

hello: Foo
       Bar

avec Foo et Bar alignés. (C’est trivial de le faire sans utiliser %n pour cet exemple particulier, et en général, on peut toujours diviser ce premier appel printf:

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

Que la commodité légèrement ajoutée vaille la peine d’utiliser quelque chose d’esotérique comme %n (et éventuellement des erreurs) est sujet à discussion.)

177
jamesdlin

Je n'ai pas vraiment vu beaucoup d'utilisations pratiques réelles du %n _ spécificateur, mais je me souviens qu'il était utilisé dans les vulnérabilités oldschool printf avec un attaque par une chaîne de format il y a longtemps.

Quelque chose qui s'est passé comme ça

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

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

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

où un utilisateur malveillant pourrait tirer parti du paramètre username transmis à printf en tant que chaîne de format et utiliser une combinaison de %d, %c ou w/e pour parcourir la pile d'appels, puis modifier la variable autorisée à une valeur vraie.

Oui, c'est une utilisation ésotérique, mais toujours utile de savoir en écrivant un démon pour éviter les failles de sécurité? :RÉ

17
Xzhsh

De ici nous voyons qu'il enregistre le nombre de caractères imprimés jusqu'à présent.

n L'argument doit être un pointeur sur un entier dans lequel est écrit le nombre d'octets écrits jusqu'à la sortie jusqu'à présent par cet appel de l'une des fonctions fprintf(). Aucun argument n'est converti.

Un exemple d'utilisation serait:

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

n_chars aurait alors une valeur de 12.

13
KLee1

L'argument associé à% n sera traité comme un int * et renseigné avec le nombre total de caractères imprimés à cet endroit dans printf.

9
Evan

Jusqu'à présent, toutes les réponses sont à ce sujet %n fait, mais pas pourquoi tout le monde le voudrait en premier lieu. Je trouve cela un peu utile avec sprintf/snprintf, lorsque vous aurez peut-être besoin de fractionner ou de modifier ultérieurement la chaîne résultante, car la valeur stockée est un index de tableau dans la chaîne résultante. Cette application est toutefois beaucoup plus utile avec sscanf, en particulier parce que les fonctions de la famille scanf ne renvoient pas le nombre de caractères traités, mais le nombre de champs.

Une autre utilisation vraiment bidon consiste à obtenir un pseudo-log10 gratuitement en même temps tout en imprimant un numéro dans le cadre d’une autre opération.

8
R..

L’autre jour, je me suis retrouvé dans une situation où %n résoudrait bien mon problème. Contrairement à ma réponse précédente , dans ce cas, je ne peux pas trouver une bonne alternative.

J'ai un contrôle graphique qui affiche du texte spécifié. Ce contrôle peut afficher une partie de ce texte en gras (ou en italique, ou souligné, etc.), et je peux spécifier quelle partie en spécifiant les index de caractère de début et de fin.

Dans mon cas, je génère le texte du contrôle avec snprintf, et j'aimerais que l'une des substitutions soit en gras. Trouver les indices de départ et d'arrivée de cette substitution n'est pas trivial pour les raisons suivantes:

  • La chaîne contient plusieurs substitutions et l'une des substitutions est un texte arbitraire, spécifié par l'utilisateur. Cela signifie que faire une recherche textuelle de la substitution qui me tient à cœur est potentiellement ambigu.

  • La chaîne de format peut être localisée et utiliser le $ Extension POSIX pour les spécificateurs de format de position. Par conséquent, la recherche de la chaîne de format d'origine pour les spécificateurs de format eux-mêmes n'est pas triviale.

  • L'aspect localisation signifie également que je ne peux pas facilement décomposer la chaîne de format en plusieurs appels à snprintf.

Par conséquent, le moyen le plus simple de trouver les indices autour d’une substitution particulière serait de faire:

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);
6
jamesdlin

Cela n'imprime rien. Il est utilisé pour déterminer le nombre de caractères imprimés avant %n est apparu dans la chaîne de format et est affiché dans l'entier fourni:

#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 pour _set_printf_count_output )

1

Il va stocker la valeur du nombre de caractères imprimés jusqu'à présent dans cette fonction printf().

Exemple:

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

Le résultat de ce programme sera

Hello World
Characters printed so far = 12
1
Sudhanshu Mishra