What is the use of the %n format specifier in C?
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 thefprintf()
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
.
josh
Updated on March 08, 2022Comments
-
josh about 2 years
What is the use of the
%n
format specifier in C? Could anyone explain with an example?-
Jens almost 10 yearsWhat has become of the fine art of reading the fine manual?
-
jia chen over 6 yearsI 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 over 5 yearsThat's why Bionic let go of it.
-
John Frazer about 4 yearsIt is in fact a valid question, and one that the fine manuals will likely not answer; it has been discovered that
%n
makesprintf
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-sublanguages
-
-
bta almost 14 yearsYou 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 almost 14 yearsOh my - this is a character-based version of computing the pixel size of string in a given font!
-
jamesdlin almost 14 years+1 for mentioning uses for
%n
, although I beg to differ about "all the answers...". =P -
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 almost 14 yearsNo, 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 almost 14 yearsYou'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 almost 11 yearsThe bad guys thank you for your use of printf/%n, sprintf, and sscanf ;)
-
jww almost 11 yearsAndroid also removed the %n specifier.
-
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 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 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 over 10 yearsCould you explain why &n and *s are needed. Are they both pointers?
-
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 ofn
. The%*s
usage in theprintf
format string prints a%s
specifier (in this case the empty string""
) using a field width ofn
characters. An explanation of basicprintf
principles is basically outside the scope of this question (and answer); I'd recommend reading theprintf
documentation or asking your own separate question on SO. -
Andrew S over 10 yearsGreat answer, Just one thing James. Why is the asterisk in the string formatter %*s.
-
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 theprintf
documentation. -
Jack almost 10 yearsSo far I know, type of
int
is implementation-independent. Shouldn't besigned int
instead of since it might beunsigned
? -
Jack almost 10 years@AndrewS: Because the function will modify the value of the variable.
-
Keith Thompson almost 10 yearsThere are more reasons than
%n
to avoid using an unchecked input string as aprintf
format string. -
jamesdlin almost 10 years@Jack
int
is always signed. -
Jack almost 10 years@jamesdlin: My mistake. I'm sorry.. I didn't know where I read that.
-
jamesdlin almost 10 years@Jack Perhaps you confused it with
char
, wherechar
,signed char
, andunsigned char
are all separate. -
Cristian Ciupitu almost 10 years
-
Johnny_D almost 10 yearsFor some reason the sample raises error with note
n format specifies disabled
. What's the reason? -
jamesdlin almost 10 years@Johnny_D See some of the other answers.
%n
is disabled on some platforms (notably Windows) for security reasons. -
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 about 9 yearsWhy there's a need to use the ampersand and why does it convert the result from the format specifier to an integer?
-
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 asscanf
. -
Mohammad Sina Karvandi over 7 yearswhen 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 over 7 yearsWhen I try your code it gives me : blah val = 36 ,,,, why 36 ?! I use a 32Bit GCC in a Windows machine
-
the_endian over 7 yearsThanks 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 almost 7 yearsI'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 sincesnprintf
returns the number of characters written. Maybe something like:int begin = snprintf(..., "blah blah %s %f yada yada", ...);
andint end = snprintf(..., "%s", ...);
and then the tail:snprintf(..., "blah blah");
. -
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 almost 5 yearsThanks 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 almost 5 yearsSeems 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 almost 5 years@PSkocik It's convoluted and error-prone enough without adding an extra level of indirection.
-
PSkocik almost 5 yearsThanks for the feedback! I think I've decided that my implementation is going to have it.
-
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 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 about 4 years@mangusta: Then it has undefined behavior because there's no
int *
argument matching the%n
. -
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 over 3 yearsWhen compiling with gcc 8.3, this example prints
blah val = 0
(on one line). -
mx0 about 2 yearsA real-world example github.com/Hamled/mazda-format-string-bug#readme