3

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?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
atul_pant
  • 99
  • 8
  • 1
    You are using `printf` incorrectly. The first argument should be a format string. Subsequent arguments should have a format specifier in the format string. For example: `printf("%s %s", s1, s2);`. I think `char *s1 "hello %s"; char *s2 = "hi"; printf(s1, s2);` would work, but it's bad practice. – Fiddling Bits Jul 20 '20 at 20:54
  • 1
    Your compiler is being helpful (and ignoring the base rule of C) in the first case, it is respecting the base rule *"the programmer knows what she is doing"* in the second case. In these snippets though, the programmer in fact did not know what she was doing (or willingly did something wrong (why???????)) – pmg Jul 20 '20 at 20:55
  • 1
    @atul_pant Please read the description of the function printf. It will be useful for you. – Vlad from Moscow Jul 20 '20 at 20:56
  • You may be thinking that `printf` is like print functions in other languages (Python, perl, etc) where you can give it many strings and it will print all of them. It is not like that. As Vlad says, you need to read its documentation, or any of the zillions of tutorials on its use that exist online. – Nate Eldredge Jul 20 '20 at 20:59
  • Formally, the first argument is supposed to be a format string, and the subsequent arguments are interpreted based on *format specifiers* embedded in the format string (like `%d`, `%s`, etc). Since your string `"hello"` doesn't contain any format specifiers, all subsequent arguments are ignored. – Nate Eldredge Jul 20 '20 at 21:01
  • When the first argument to `printf` is a string literal, the compiler will try to check that it makes sense with the rest of the arguments. But when it's a variable, the compiler doesn't try to do that, because tracing back to find its value can be difficult or impossible. – Nate Eldredge Jul 20 '20 at 21:02
  • Does this answer your question? [Is calling printf with excess arguments undefined behaviour?](https://stackoverflow.com/questions/31559109/is-calling-printf-with-excess-arguments-undefined-behaviour) – Language Lawyer Jul 20 '20 at 22:30

4 Answers4

7

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.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
4

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".

einpoklum
  • 118,144
  • 57
  • 340
  • 684
1

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");
Acorn
  • 24,970
  • 5
  • 40
  • 69
1

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)));
0___________
  • 60,014
  • 4
  • 34
  • 74