printf with multiple pointers as arguments in C

Viewed 313

What is the behavior of printf() when we supply multiple arguments to it without a format specifier?

Example:

int main() 
{
    printf("hello", "hi");
    return 0;
}

Why does the compiler produce a warning on compilation of the above program? :

warning: too many arguments for format [-Wformat-extra-args]

If we compile the similar program below:

int main() 
{
    char *s1 = "hello";
    char *s2 = "hi";
    printf(s1, s2);
}

No warnings are produced. What is the reason for this?

Also, why do both programs output hello only, and don't also print hi?

4 Answers

The C 2018 standard specifies the behavior of printf in clause 7.21.6.3, in which paragraph 2 says “The printf function is equivalent to fprintf with the argument stdout interposed before the arguments to printf.”

The standard specifies the behavior of fprintf in 7.21.6.1, which tells us the second argument (the first argument of printf) is a format string and that it may contain various conversion specifications introduced by the character “%”. Thus, in printf("hello", "hi"), "hello" is a format string with no conversion specifications. In this case, paragraph 2 tells us what happens:

If the format is exhausted [fully processed] while arguments remain, the excess arguments are evaluated (as always) but are otherwise ignored.

Thus, in printf("hello", "hi"), "hi" is ignored, and "hello" is a format string that contains only ordinary characters, which are copied to the output stream per paragraph 3.

The compiler warns about printf("hello", "hi") because it is able to see that this call contains an excess argument because the format string does not contain a conversion specification for it.

Your compiler does not warn about printf(s1,s2); because it does not analyze what s1 will contain during this call. This sort of analysis is not impossible in this situation, but situations like this are rare: When a programmer uses a pointer to a string as the format string for printf, it is usually a string or pointer that is computed, constructed, or selected during program execution, and the manner of this computation is often beyond the ability of a compiler to analyze. Situations where the pointer is clearly a pointer to a fixed string are rare, since they are not frequently useful, so presumably compiler implementors have not found it valuable to implement the code necessary for the compiler to handle these situations.

tl;dr: Extra arguments to printf() are ignored.

The official C language standard (the link is to a draft of the C11 version) says the following:

§ 7.21.6.1 The fprintf function

  1. ...

  2. ... If the format is exhausted while arguments remain, the excess arguments are evaluated (as always) but are otherwise ignored. The fprintf function returns when the end of the format string is encountered.

... and printf() is simply fprintf() targeted at the standard output file.

About your two code snippets:

  • The compiler is giving you a hint, for the first snippet, that the number of arguments doesn't match the number of specifiers in the format string. It's just a courtesy - it's not required to notice this. This also explains why the compiler does not notice it for the second snippet. It could, but it's too much effort to chase your pointers and check what they point at.

  • In both cases, your format string is your first argument to printf(), i.e. "hello". That string has no format specifiers, so the printf() looks at the "hello", and understands it only needs to print that and doesn't need process any other arguments. That's whi it ignores "hi".

The first parameter of printf is the format string, because printf is about printing formatted data. To specify how to format the data, printf uses the first argument. This is different from other languages and libraries where all the parameters (like Python's print) are used in the same way and formatting is done through other means.

The first and second examples you provide are both "incorrect" although technically valid because you are passing a format string that does not need any extra argument, so "hi" is unused.

What you may want to do instead is:

printf("%s %s", "hello", "hi");

many compilers know well very well the printf function family and read compile time the format string analysing the parameters. printf("hello",s2); compiler see that there is no %... in the format string and does not expect any other parameters. Warning is issued

if you call printf(s1,s2); compiler does not know what is the content of the s1 and it cannot go through the format string and no warning issued.

Many compilers have special extension to inform them that your function is printf like and you want compiler to read the format string - gcc:

      extern int
      my_printf (void *my_object, const char *my_format, ...)
            __attribute__ ((format (printf, 2, 3)));
Related