-->

How to wisely interpret this compiler warning?

2019-04-28 07:26发布

问题:

When I executed the code of this question, I got this warning:

warning: format '%d' expects argument of type 'int', but argument 2 has type 'long int' [-Wformat=]
printf("P-Q: %d, P: %d, Q: %d", (p - q), p, q);
             ~^                 ~~~~~~~
             %ld

As a reflex fix, I used %ld to print the subtraction of two pointers. And the compiler agreed.

Fortunately, I saw a comment from another user mentioning that %td should be used, since the result type of the subtraction is ptrdiff_t. This answer confirms this claim.

Now from GCC's header file of stddef.h, I can see that these types are equivalent in this case:

typedef __PTRDIFF_TYPE__ ptrdiff_t;
#define __PTRDIFF_TYPE__ long int

However, I was just going to suggest a wrong (more or less) fix to the OP, with %ld, instead of %td.

Is there a way I could have understood that the compiler warning alone was not enough? Or maybe to wisely have interpreted the warning itself, and not just react.

回答1:

I don't think you can tell. It depends on the intent/caution/smartness of the compiler writer.

Maybe he decided he would always support %ld where %td is expected, or maybe he was just unaware/unable/unwilling to give a more detailed/proper message. In case of doubt, your last resort is the standard.

This doesn't seem to be a portable construct and for "orthodoxy" you should support both format specifiers.



回答2:

The key here is: don't do any form of arithmetic inside printf in the first place. Separate algorithm from GUI.

Code such as printf("%d", p - q) is very dangerous, not just because you might get the types wrong logically, but also since C might "do you a favour" and silently change the types through implicit type promotion. Examples.

In addition, most compilers don't warn for wrong format specifiers. This is a relatively new thing in the history of C, since compilers aren't required to show a diagnostic message here. It is just a bonus feature of gcc.

How to avoid bugs? These functions are inherently dangerous - that's just how it is and everyone knows it. Probably printf & scanf family of functions are the most harmful functions ever written in the history of programming, in terms of total bug cost caused to mankind. So what you should do to:

  • Avoid stdio.h if possible and keep it away from production-quality code. Portability is not always more important than robust code - sometimes it is preferable to use the raw console API. Avoid variable argument list functions in general.
  • If not possible to avoid, wrap the "GUI" part of stdio.h inside a separate file, which you should be doing anyway. Don't mix printing/input with algorithms. Make an interface which is using pointers.
  • It is 2018, not 1970: don't write console interfaces in the first place. Ye ye I know... there's lots of old crap still floating around which needs to be maintained. But nowadays, console functions should be used mostly for debugging purposes and for newbies learning C, in which case type safety might not be such a big issue.