What is the use of the %n format specifier in C?

115,742

Solution 1

Nothing printed. The argument must be a pointer to a signed int, where the number of characters written so far is stored.

#include <stdio.h>

int main()
{
  int val;

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

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

  return 0;

}

The previous code prints:

blah  blah
val = 5

Solution 2

Most of these answers explain what %n does (which is to print nothing and to write the number of characters printed thus far to an int variable), but so far no one has really given an example of what use it has. Here is one:

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

will print:

hello: Foo
       Bar

with Foo and Bar aligned. (It's trivial to do that without using %n for this particular example, and in general one always could break up that first printf call:

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

Whether the slightly added convenience is worth using something esoteric like %n (and possibly introducing errors) is open to debate.)

Solution 3

I haven't really seen many practical real world uses of the %n specifier, but I remember that it was used in oldschool printf vulnerabilities with a format string attack quite a while back.

Something that went like this

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

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

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

where a malicious user could take advantage of the username parameter getting passed into printf as the format string and use a combination of %d, %c or w/e to go through the call stack and then modify the variable authorized to a true value.

Yeah it's an esoteric use, but always useful to know when writing a daemon to avoid security holes? :D

Solution 4

So far all the answers are about that %n does, but not why anyone would want it in the first place. I find it's somewhat useful with sprintf/snprintf, when you might need to later break up or modify the resulting string, since the value stored is an array index into the resulting string. This application is a lot more useful, however, with sscanf, especially since functions in the scanf family don't return the number of chars processed but the number of fields.

Another really hackish use is getting a pseudo-log10 for free at the same time while printing a number as part of another operation.

Solution 5

From here we see that it stores the number of characters printed so far.

n The argument shall be a pointer to an integer into which is written the number of bytes written to the output so far by this call to one of the fprintf() functions. No argument is converted.

An example usage would be:

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

n_chars would then have a value of 12.

Share:
115,742
josh
Author by

josh

Updated on March 08, 2022

Comments

  • josh
    josh about 2 years

    What is the use of the %n format specifier in C? Could anyone explain with an example?

    • Jens
      Jens almost 10 years
      What has become of the fine art of reading the fine manual?
    • jia chen
      jia chen over 6 years
      I think the real question is what is the POINT of the an option like this? why would anyone want to know the value of the numbers of char printed much less write that value directly to memory. It was like the developers were bored and decided to introduce a bug into the kernal
    • solidak
      solidak over 5 years
      That's why Bionic let go of it.
    • John Frazer
      John Frazer about 4 years
      It is in fact a valid question, and one that the fine manuals will likely not answer; it has been discovered that %n makes printf accidentally Turing-complete and you can e.g. implement Brainfuck in it, see github.com/HexHive/printbf and oilshell.org/blog/2019/02/07.html#appendix-a-minor-sublangua‌​ges
  • bta
    bta almost 14 years
    You mention that the argument must be a pointer to a signed int, then you used an unsigned int in your example (probably just a typo).
  • Admin
    Admin almost 14 years
    Oh my - this is a character-based version of computing the pixel size of string in a given font!
  • jamesdlin
    jamesdlin almost 14 years
    +1 for mentioning uses for %n, although I beg to differ about "all the answers...". =P
  • jamesdlin
    jamesdlin almost 14 years
    %n existed in C89. It doesn't work with MSVC because Microsoft disabled it by default for security concerns; you must call _set_printf_count_output first to enable it. (See Merlyn Morgan-Graham's answer.)
  • user411313
    user411313 almost 14 years
    No, C89 defines not this feature/backdoorbug. See K&R+ANSI-C amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/… ??where is the URL-tagger for comments??
  • jamesdlin
    jamesdlin almost 14 years
    You're simply wrong. It's listed plainly in Table B-1 (printf conversions) of Appendix B of K&R, 2nd edition. (Page 244 of my copy.) Or see section 7.9.6.1 (page 134) of the ISO C90 specification.
  • jww
    jww almost 11 years
    The bad guys thank you for your use of printf/%n, sprintf, and sscanf ;)
  • jww
    jww almost 11 years
    Android also removed the %n specifier.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE almost 11 years
    @noloader: How so? Use of %n has absolutely zero danger of vulnerability to an attacker. The misplaced infamy of %n really belongs on the stupid practice of passing a message string rather than a format string as the format argument. This situation of course never arises when %n is actually part of an intentional format string being used.
  • jww
    jww almost 11 years
    %n allows you to write to memory. I think you are assuming that the attacker does not control that pointer (I could be wrong). If the attacker controls the pointer (it just another parameter to printf), he/she could perform a write of 4 bytes. Whether he/she can profit is a different story.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE almost 11 years
    @noloader: That's true about any use of pointers. Nobody says "bad guys thank you" for writing *p = f();. Why should %n, which is just another way of writing a result to the object pointed to by a pointer, be considered "dangerous", rather than considering the pointer itself dangerous?
  • Andrew S
    Andrew S over 10 years
    Could you explain why &n and *s are needed. Are they both pointers?
  • jamesdlin
    jamesdlin over 10 years
    @AndrewS &n is a pointer (& is the address-of operator); a pointer is necessary because C is pass-by-value, and without a pointer, printf could not modify the value of n. The %*s usage in the printf format string prints a %s specifier (in this case the empty string "") using a field width of n characters. An explanation of basic printf principles is basically outside the scope of this question (and answer); I'd recommend reading the printf documentation or asking your own separate question on SO.
  • Andrew S
    Andrew S over 10 years
    Great answer, Just one thing James. Why is the asterisk in the string formatter %*s.
  • jamesdlin
    jamesdlin over 10 years
    @AndrewS Because that's how the printf syntax was designed. Basically the * in %*s means to use one of the arguments as a field width. Please read the printf documentation.
  • Jack
    Jack almost 10 years
    So far I know, type of int is implementation-independent. Shouldn't be signed int instead of since it might be unsigned?
  • Jack
    Jack almost 10 years
    @AndrewS: Because the function will modify the value of the variable.
  • Keith Thompson
    Keith Thompson almost 10 years
    There are more reasons than %n to avoid using an unchecked input string as a printf format string.
  • jamesdlin
    jamesdlin almost 10 years
    @Jack int is always signed.
  • Jack
    Jack almost 10 years
    @jamesdlin: My mistake. I'm sorry.. I didn't know where I read that.
  • jamesdlin
    jamesdlin almost 10 years
    @Jack Perhaps you confused it with char, where char, signed char, and unsigned char are all separate.
  • Cristian Ciupitu
    Cristian Ciupitu almost 10 years
  • Johnny_D
    Johnny_D almost 10 years
    For some reason the sample raises error with note n format specifies disabled. What's the reason?
  • jamesdlin
    jamesdlin almost 10 years
    @Johnny_D See some of the other answers. %n is disabled on some platforms (notably Windows) for security reasons.
  • smwikipedia
    smwikipedia over 9 years
    @AndrewS "integer value or * that specifies minimum field width. The result is padded with space characters (by default)... In the case when * is used, the width is specified by an additional argument of type int." from "en.cppreference.com/w/c/io/fprintf"
  • shinzou
    shinzou about 9 years
    Why there's a need to use the ampersand and why does it convert the result from the format specifier to an integer?
  • jamesdlin
    jamesdlin about 9 years
    @kuhaku The ampersand is necessary because it's an output parameter, so you need to pass a pointer to printf. It's the same reason why you must pass pointers to functions such as scanf.
  • Mohammad Sina Karvandi
    Mohammad Sina Karvandi over 7 years
    when I try you code it gives me : Hello World Characters printed so far = 36 ,,,,, why 36 ?! I use a 32bit GCC in a windows machine.
  • Mohammad Sina Karvandi
    Mohammad Sina Karvandi over 7 years
    When I try your code it gives me : blah val = 36 ,,,, why 36 ?! I use a 32Bit GCC in a Windows machine
  • the_endian
    the_endian over 7 years
    Thanks for showing a use-case. I don't understand why people just basically copy-paste the manual into SO and reword it sometimes. We're humans and everything is done for a reason which should always be explained in an answer. "Does nothing" is like saying "The word cool means cool" - almost useless knowledge.
  • jww
    jww almost 7 years
    I'll give you +1 for the use case. But you are going to fail an audit, so you should probably devise another way to mark the begin and end of the bold text. It seems like three snprintf while checking return values will work just fine since snprintf returns the number of characters written. Maybe something like: int begin = snprintf(..., "blah blah %s %f yada yada", ...); and int end = snprintf(..., "%s", ...); and then the tail: snprintf(..., "blah blah");.
  • jamesdlin
    jamesdlin almost 7 years
    @jww The problem with multiple snprintf calls is that the substitutions might be rearranged in other locales, so they cannot be broken up like that.
  • PSkocik
    PSkocik almost 5 years
    Thanks for the example. But couldn't you, like, write an terminal control sequence to make the output bold right before the field and then write a sequence after it? If you don't hardcode the terminal control sequences, you could make them positional (reorderable) too.
  • PSkocik
    PSkocik almost 5 years
    Seems like this could be made more useful if the format string allowed specifying pointers to sizes where as width/precision specifiers. Then you could just make one printf call.
  • jamesdlin
    jamesdlin almost 5 years
    @PSkocik It's convoluted and error-prone enough without adding an extra level of indirection.
  • PSkocik
    PSkocik almost 5 years
    Thanks for the feedback! I think I've decided that my implementation is going to have it.
  • jamesdlin
    jamesdlin almost 5 years
    @PSkocik If you're outputting to a terminal. If you're working with, say, a Win32 rich-edit control, that won't help unless you want to go back and parse the terminal control sequences afterward. That also assumes that you want to honor terminal control sequences in the rest of the substituted text; if you don't, then you'd have to filter or escape those. I'm not saying it's impossible to do without %n; I'm claiming that using %n is more straightforward than alternatives.
  • mangusta
    mangusta about 4 years
    @R..GitHubSTOPHELPINGICE what will happen if the format string contains "%n" and no other arguments are passed into "printf"? for instance, printf ("blah%n") ? Is 4 written somewhere?
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE about 4 years
    @mangusta: Then it has undefined behavior because there's no int * argument matching the %n.
  • mangusta
    mangusta about 4 years
    @R..GitHubSTOPHELPINGICE it is undefined, but technically doesn't it try to store 4 into the address referred by 1st argument (which wasn't passed and hence is random) on the user stack?
  • René Nyffenegger
    René Nyffenegger over 3 years
    When compiling with gcc 8.3, this example prints blah val = 0 (on one line).
  • mx0
    mx0 about 2 years